zoukankan      html  css  js  c++  java
  • 最短路综合

    最短路综合


    前言:

       最短路扩展例题。

     


    例题:

      奇怪建图的最短路:

      POJ3767

      在一个国家有两个group,记做1和2,N个city,每个city属于1或者2。每两个city间有一定的距离,现在要从city1去city2,问最短的距离是多少,要求至多只有一次穿越时跨过分属不同group的city。city1总是属于group1,city2总属于group2。

      分析:同一城市内建双向边,city1到city2建单向边,注意无解。

      特殊最短路计数:

      洛谷P2505

      有向图,求每条边被多少条最短路经过

      分析:定理:任意一条最短路的某个子路径都是最短路(懒得证,脑补吧)

      枚举起点,先跑一遍dj(珍爱生命,远离spfa),把所有属于最短路的边标记,形成最短路图,必定是有向无环图。

      考虑到每一条边的贡献为两端端点的最短路条数相乘;

      拓扑排序,先求cnt1[ i ]表示从起点到 i 节点的最短路条数,易得如果cnt1[ st ]=1且存在边从u到v 那么cnt1[ v ] += cnt1[ u ];

      再求从cnt2[ i ]表示以 i 为起点的最短路条数,cnt2[ i ]初始值为1,若存在边从u到v,则cnt2[ u ]+=cnt2[ v ];

      每条边每次贡献为cnt1[ u ] * cnt2[ v ];

      POJ3613

      给定一张带权图,求从s到t的恰好经过k条边的最短路径长度

      分析:用一个矩阵表示初始经过一条边a_ij的长度,没有边是正无穷

      矩阵乘法:a矩阵表示经过n条边,b矩阵表示经过m条边,a*b矩阵则表示经过n+m条边,采用了floyd的思想

      矩阵快速幂即可

      证明:不会,但是会甩链接《矩阵乘法在信息学的应用》

      拓展:求s到t长度为k的方案数:

      矩阵a [ d ][ i ][ j ]为从i到j长度为d的方案数;

      分层图最短路:

      有一种图论模型:在图上可以进行k次特殊决策,每次决策不改变图的结构,只改变当前状态,我们称之为分层图最短路。

      洛谷P4568

      有k次免费机会的最短路

      分析:我们在存储每一个状态时多存储一个变量f表示当前已经使用了几次免费机会,dis[ i ][ j ]表示到 i 节点用了 j 次机会的最短路长度;

      每次决策分两部分:

      第一部分:不使用机会,直接莽过去;

      第二部分:使用机会,用特殊代价转移状态;

      

    #include<bits/stdc++.h>
    using namespace std;
    #define int long long
    inline int read()
    {
        int x=0,f=1;
        char ch;
        for(ch=getchar();(ch<'0'||ch>'9')&&ch!='-';ch=getchar());
        if(ch=='-') f=0,ch=getchar();
        while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
        return f?x:-x;
    }
    int n,m,k,res=1e15;
    int st,ed;
    int dis[100010][21];
    bool vis[10010][21];
    int head[100010],cnt;
    struct point
    {
        int nxt,to,val;
    }a[100010];
    struct p
    {
        int now,fre,dis;
        bool operator <(const p &a) const
        {
            return dis>a.dis;
        }
    };
    priority_queue<p> q; 
    inline void add(int x,int y,int z)
    {
        a[++cnt].nxt=head[x];
        a[cnt].to=y;
        a[cnt].val=z;
        head[x]=cnt;
    }
    inline void dj()
    {
        memset(dis,0x3f,sizeof(dis));
        dis[st][0]=0;
        q.push((p){st,0,0});
        while(!q.empty())
        {
            int now=q.top().now;
            int f=q.top().fre;
            q.pop();
            if(vis[now][f]) continue;
            vis[now][f]=1;
            for(int i=head[now];i;i=a[i].nxt)
            {
                int t=a[i].to;
                if(f<k&&dis[t][f+1]>dis[now][f])
                {
                    dis[t][f+1]=dis[now][f];
                    if(t==ed)    res=min(res,dis[t][f+1]);
                    q.push((p){t,f+1,dis[t][f+1]});
                }
                if(dis[t][f]>dis[now][f]+a[i].val)
                {
                    dis[t][f]=dis[now][f]+a[i].val;
                    if(t==ed) res=min(res,dis[t][f]);
                    q.push((p){t,f,dis[t][f]});
                }
            }
        }
    }
    signed main()
    {
        n=read(),m=read(),k=read();
        st=read(),ed=read();
        for(int x,y,z,i=1;i<=m;++i)
        {
            x=read(),y=read(),z=read();
            add(x,y,z);
            add(y,x,z);
        }
        dj();
        printf("%lld
    ",res);
    return 0;
    }

      洛谷P3831

      一个网格图,走一条边花费为2,在某些点转向花费为1,求最短路;

      分析状态:位置,方向。与其他最短路不同的就是方向。

      考虑到往回走肯定不优,所以我们只需要记录是横向还是纵向即可。

      考虑到n较大,我们只记录有用的节点(转向点和起点终点),将所有有用点记录在结构体中,设每个点的编号为(x-1)*n+y;

      1.按编号排序,离散化;

      2.按x排序,x相等按y排序,把x相等的相邻两个点之间连上边;

      3.按y排序,y相等按x排序,把y相等的相邻两个点之间连上边;

      跑dj(珍爱生命,远离spfa)

      (数据太水没有无法到达的情况,实际上注意判断,虽然我没判就过了)

      

    #include<bits/stdc++.h>
    using namespace std;
    #define int long long
    inline int read()
    {
        int x=0,f=1;
        char ch;
        for(ch=getchar();(ch<'0'||ch>'9')&&ch!='-';ch=getchar());
        if(ch=='-') f=0,ch=getchar();
        while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
        return f?x:-x;
    }
    int n,m,res=1e18;
    int stx,sty,edx,edy,tot,st,ed;
    int dis[200010][3];
    bool vis[200010][3];
    int head[233333],cnt;
    struct node
    {
        int x,y,id;
    }sub[200010];
    struct p
    {
        int dis,ch,now;
        bool operator<(const p &a) const
        {
            return dis>a.dis;
        }
    };
    priority_queue<p> q;
    struct point
    {
        int nxt,to,val;
        int ch;
    }a[500010];
    inline void add(int x,int y,int z,int ch)
    {
        a[++cnt].nxt=head[x];
        a[cnt].to=y;
        a[cnt].val=z;
        a[cnt].ch=ch;
        head[x]=cnt;
    }
    inline bool cmp(node a,node b)
    {
        return a.id<b.id;
    }
    inline bool cmp1(node a,node b)
    {
        return (a.x==b.x)?a.y<b.y:a.x<b.x;
    }
    inline bool cmp2(node a,node b)
    {
        return (a.y==b.y)?a.x<b.x:a.y<b.y;
    }
    inline void dj()
    {
        memset(dis,0x3f,sizeof(dis));
        for(int i=1;i<=2;++i)
        {
            dis[st][i]=0;
            q.push((p){0,i,st});
        }
        while(!q.empty())
        {
            int now=q.top().now;
            int ch=q.top().ch;
            q.pop();
            if(vis[now][ch]) continue;
            vis[now][ch]=1;
            for(int i=head[now];i;i=a[i].nxt)
            {
                int t=a[i].to;
                int f=a[i].ch;
                if(dis[t][f]>dis[now][ch]+a[i].val+(f!=ch))
                {
                    dis[t][f]=dis[now][ch]+a[i].val+(f!=ch);
                    if(t==ed) res=min(res,dis[t][f]);
                    q.push((p){dis[t][f],f,t});
                }
            }
        }
    }
    signed main()
    {
        n=read(),m=read();
        for(int x,y,i=1;i<=m;++i)
        {
            x=read(),y=read();
            sub[++tot].x=x;
            sub[tot].y=y;
            sub[tot].id=(x-1)*n+y;
        }
        stx=read(),sty=read(),edx=read(),edy=read();
        sub[++tot].x=stx;
        sub[tot].y=sty;
        sub[tot].id=(stx-1)*n+sty;
        sub[++tot].x=edx;
        sub[tot].y=edy;
        sub[tot].id=(edx-1)*n+edy;
        sort(sub+1,sub+tot+1,cmp);
        for(int i=1;i<=tot;++i)
        {
            if(sub[i].id==(stx-1)*n+sty)
            {
                sub[i].id=i,st=i;
            }
            else if(sub[i].id==(edx-1)*n+edy)
            {
                sub[i].id=i,ed=i;
            }
            else sub[i].id=i;
        }
        sort(sub+1,sub+tot+1,cmp1);
        for(int i=1;i<=tot;++i)
        {
            if(sub[i-1].x==sub[i].x)
            {
                add(sub[i].id,sub[i-1].id,(sub[i].y-sub[i-1].y)*2,1);
                add(sub[i-1].id,sub[i].id,(sub[i].y-sub[i-1].y)*2,1);
            }
        }
        sort(sub+1,sub+tot+1,cmp2);
        for(int i=1;i<=tot;++i)
        {
            if(sub[i-1].y==sub[i].y)
            {
                add(sub[i].id,sub[i-1].id,(sub[i].x-sub[i-1].x)*2,2);
                add(sub[i-1].id,sub[i].id,(sub[i].x-sub[i-1].x)*2,2);
            }
        }
        dj();
        //-1
        printf("%lld
    ",res);
    return 0;
    }

      次短路

       洛谷P2865

      求严格次短路。

      方法1:dis[ i ][ 1 ]为到 i 节点最短路值,dis [ i ][ 2 ]为到 i 节点次短路值;

      这个用spfa,dj我每研究出按什么排序比较好

      

    inline void spfa()
    {
        for(int i=1;i<=n;++i) dis[i][1]=dis[i][2]=1e15;
        dis[1][1]=0;
        q.push(1);
        while(!q.empty())
        {
            int now=q.front();
            q.pop();
            vis[now]=0;
            for(int i=head[now];i;i=a[i].nxt)
            {
                int t=a[i].to;
                bool flag=0;
                if(dis[t][1]>dis[now][1]+a[i].val)
                {
                    dis[t][2]=dis[t][1];
                    dis[t][1]=dis[now][1]+a[i].val;
                    flag=1;
                }
                if(dis[t][2]>dis[now][1]+a[i].val&&dis[t][1]!=dis[now][1]+a[i].val)
                {
                    dis[t][2]=dis[now][1]+a[i].val,flag=1;
                }
                if(dis[t][2]>dis[now][2]+a[i].val)
                {
                    dis[t][2]=dis[now][2]+a[i].val;
                    flag=1;
                }
                if(flag&&!vis[t])
                {
                    vis[t]=1;
                    q.push(t);
                }
            }
        }
    }

      方法2:求出最短路和路径,枚举删边;

      方法3:A*,不会;

      特殊最短路模型

      POJ3013

      给定一张无向图, 要求一棵最小生成树。 根结点确定为1,权值的定义为:每个结点都有自己的权值,每条边有长度, 构建每条边的花费是,此边的单价 * 所有后代的结点权重之和。

      转而考虑每个节点花费:该节点权值 * 节点到根的距离;

      从1号节点跑一遍最短路就行了

      妙啊

      基础图论大杂烩

      BZOJ4144(权限题,自己找吧qwq)

      n个点m条边的带权无向图,有s个点是加油站,每辆车有个油量上限c,保证x和y是加油站,能否从x走到y;

      分析:我们发现除了加油站,其他点都是垃圾都没有用,所以在加油站之间搞MST,然后货车运输就好了(别告诉我你不会货车运输

      怎么确定边权呢?

      我们记 f [ i ]为距离 i 号节点最近的加油站;

      考虑如果从x到y的路径上出现一个点k,f[ k ]属于z,那么从x到z,再用z到y一定更优;

      把所有加油站都扔进堆里面跑一遍dj,求出所有点的dis和 f ;

      枚举每一条边,如果有一条边两端的节点属于两个不同的加油站,那么那么就在待定最小生成树图上增加一条连接u,v的边,权值为dis[u]+dis[v]+len(u,v);

  • 相关阅读:
    笔记
    【转】并行类加载——让tomcat玩转双十一 @双十一实战
    Twitter Snowflake 生成唯一id java版本
    nginx添加 nginx_heath模块
    谁在关心toString的性能?
    HashMap的工作原理
    修改nginx版本名称伪装任意web server
    nginx concat模块配置 页面返回400 bad request
    linux 安装rz sz命令
    nginx 添加nginx-http-concat模块
  • 原文地址:https://www.cnblogs.com/knife-rose/p/11271688.html
Copyright © 2011-2022 走看看