zoukankan      html  css  js  c++  java
  • 分层图最短路

    分层图最短路

      一个听起来就很高端的词,其实也没有听起来那么可怕啦。

      关于这道题的小故事:loli说要从头讲输入输出!于是我们被赶到了高一高二的机房,学姐说:我给你推荐道题吧...

      我自己想到这个做法的时候是这么做的,将所有的点,所有的边都建出来,非常好做,但是占的内存比较大。其实,因为每一层图非常相似,所以可以用一个二维数组直接做最短路。

      $dp[i][j]$表示第$i$个点,第$j$层的最短路。用$i$层更新第$i+1$层就可以啦。

      

      [SHOI2012]回家的路:https://www.luogu.org/problemnew/show/P3831

      题意概述:在一个$n*n$的网格图中跑地铁,每坐一站路就耗2分钟,如果想从横向改为纵向,必须从一些特殊的点进行换乘,每次换乘耗时1分钟。给定起点终点,求最短路。

      多么有趣的题目啊!又是多么的难啊!一开始想了一个很奇妙的思路,开两个Dijkstra互相跑,然而我的思路很乱并没有成功。

      写了一个小时后放弃信心开始胡思乱想,想到以前有一次坐火车从东站到西站要跨越整个城市,感觉就像是两个火车站一样呢...两个火车站吗?奇迹般的想到了怎么做这道题。

      把每一个换乘点拆成两个点,一个点负责跑横向路,一个点负责跑纵向路,两个点之间再连一条边,为换乘代价,这样就转化成了一个普通的最短路问题。注意$firs$数组要开到$m$的两倍以上,邻接表要开到$m$的五倍以上(每个点拆开后连两条边,每个点再向外连出两条边)。还有一点小的细节,起点和终点拆开后须连一条边权为0的边(因为本来就是一个点)。

      这道题...我用正解思路得了暴力分60,为什么呢?(其实是因为建图)一开始怎么也想不出怎么连边才比较快,于是就用了$m^{2}$,后来才想起来先排个序啊...

       
    # include <cstdio>
    # include <iostream>
    # include <cstring>
    # include <queue>
    # include <algorithm>
    
    using namespace std;
    
    const int maxm=100005;
    const int inf=1e8;
    
    struct edge
    {
        int nex,too,co;
    }g[maxm*15];
    
    struct poin
    {
        int r;
        int x,y;
    }M[maxm];
    
    int h=0,ld,las,n,m,x,y,firs[maxm<<1];
    int d[maxm*2+5];
    bool vis[maxm*2+5]={false};
    int sx,sy,tx,ty;
    queue <int> q;
    
    void add(int x,int y,int co)
    {
        g[++h].co=co;
        g[h].too=y;
        g[h].nex=firs[x];
        firs[x]=h;
        g[++h].co=co;
        g[h].too=x;
        g[h].nex=firs[y];
        firs[y]=h;
        return ;
    }
    
    void dis(int s)
    {
        for (int i=0;i<=m*2+4;i++)
            d[i]=inf;
        d[s]=0;
        vis[s]=1;
        q.push(s);
        int beg,j;
        while (q.size())
        {
            beg=q.front();
            q.pop();
            vis[beg]=0;
            for (int i=firs[beg];i;i=g[i].nex)
            {
                j=g[i].too;
                if(d[beg]+g[i].co>=d[j]) continue;
                d[j]=d[beg]+g[i].co;
                if(!vis[j])
                {
                    vis[j]=1;
                    q.push(j);
                }
            }
        }
    }
    
    bool cmpa(poin a,poin b)
    {
        if(a.x==b.x) return a.y<b.y;
        return a.x<b.x;
    }
    
    bool cmpb(poin a,poin b)
    {
        if(a.y==b.y) return a.x<b.x;
        return a.y<b.y;
    }
    
    int main()
    {
        scanf("%d%d",&n,&m);
        for (int i=1;i<=m;i++)
        {
             scanf("%d%d",&M[i].x,&M[i].y);
            M[i].r=i;
        }
        scanf("%d%d%d%d",&sx,&sy,&tx,&ty);
        
        int aaa=10000000,bbb,ccc=10000000,ddd;
        sort(M+1,M+1+m,cmpa);
        for (int i=1;i<=m;i++)
        {
            if(M[i].x==sx)
            {
                if(max(M[i].y-sy,sy-M[i].y)*2<=aaa)
                    aaa=min(aaa,max(M[i].y-sy,sy-M[i].y)*2),bbb=M[i].r;
            }
            if(M[i].x==tx)
            {
                if(max(M[i].y-ty,ty-M[i].y)*2<=ccc)
                    ccc=min(ccc,max(M[i].y-ty,ty-M[i].y)*2),ddd=M[i].r;
            }
            add(M[i].r,M[i].r+m+2,1);
            if(M[i-1].x!=M[i].x) continue;
            add(M[i].r,M[i-1].r,(M[i].y-M[i-1].y)*2);
        }
        if(aaa!=10000000) add(0,bbb,aaa);
        if(ccc!=10000000) add(m+1,ddd,ccc);
        
        sort(M+1,M+1+m,cmpb);
        aaa=10000000;
        ccc=10000000;
        for (int i=1;i<=m;i++)
        {
            if(M[i].y==sy)
            {    
                if(max(M[i].x-sx,sx-M[i].x)*2<=aaa)
                    aaa=min(aaa,max(M[i].x-sx,sx-M[i].x)*2),bbb=M[i].r+m+2;
            }
            if(M[i].y==ty)
            {    
                if(max(M[i].x-tx,tx-M[i].x)*2<=ccc)
                    ccc=min(ccc,max(M[i].x-tx,tx-M[i].x)*2),ddd=M[i].r+m+2;
            }
            if(M[i-1].y!=M[i].y) continue;
            add(M[i].r+m+2,M[i-1].r+m+2,(M[i].x-M[i-1].x)*2);
        }
        if(aaa!=10000000) add(m+2,bbb,aaa);
        if(ccc!=10000000) add(2*m+3,ddd,ccc);
        
        if(sx==tx)
        {
            printf("%d",max(sy-ty,ty-sy)*2);    
            return 0;
        }
        if(sy==ty)
        {
            printf("%d",max(sx-tx,tx-sx)*2);
            return 0;
        }
        
        add(0,m+2,0);
        add(2*m+3,m+1,0);
        dis(0);
        if(inf!=d[m+1])
            printf("%d",d[m+1]);
        else
            printf("-1");
        return 0;
    }
    回家的路

       发现分层图真是个好东西,终于解决这道题后就去学了一下。


      

      时隔半个月我又回来写分层图啦!

      [JLOI2011]飞行路线:https://www.luogu.org/problemnew/show/P4568

      题意概述:给定一张无向图,可以将其中$k$条边的权值改为$0$,求$1$到$n$的最短路。(k<=10)

      $k$非常小啊,于是可以拆点,把一个点强行拽成k个,然后就可以连边了。怎么连呢?首先原来就有的边是不能不连的,而且还要在每一层图上都连。接下来就要确定每层图之间的关系了,从第i层到第i+1层的边边权全为0,等于说是用掉了一次免费卡,于是愉快的连一连,跑一跑堆优化$dijktra$,这道题就做完啦。

      
     1 // luogu-judger-enable-o2
     2 # include <cstdio>
     3 # include <iostream>
     4 # include <queue>
     5 # include <cstring>
     6 # define mp make_pair
     7 # define R register int
     8 
     9 using namespace std;
    10 
    11 int h,n,m,k,s,t,a,b,c,firs[220009];
    12 struct edge
    13 {
    14     int co,too,nex;
    15 }g[4200009];
    16 int d[220009];
    17 bool vis[220009];
    18 typedef pair <int,int> pii;
    19 priority_queue <pii,vector<pii>,greater<pii> > q;
    20 
    21 void add(int x,int y,int co)
    22 {
    23     g[++h].too=y;
    24     g[h].co=co;
    25     g[h].nex=firs[x];
    26     firs[x]=h;
    27 }
    28 
    29 void dis()
    30 {
    31     memset(d,127,sizeof(d));
    32     d[s]=0;
    33     q.push(mp(d[s],s));
    34     int beg,j;
    35     while (q.size())
    36     {
    37         beg=q.top().second;
    38         q.pop();
    39         if(vis[beg]) continue;
    40         vis[beg]=true;
    41         for (R i=firs[beg];i;i=g[i].nex)
    42         {
    43             j=g[i].too;
    44             if(d[beg]+g[i].co>=d[j]) continue;
    45             d[j]=d[beg]+g[i].co;
    46             q.push(mp(d[j],j));
    47         }
    48     }
    49 }
    50 
    51 int main()
    52 {
    53     scanf("%d%d%d",&n,&m,&k);
    54     for (R i=1;i<=m;++i)
    55     {
    56         scanf("%d%d%d",&a,&b,&c);
    57         add(a,b,c);
    58         add(b,a,c);
    59         for (R j=1;j<=k;++j)
    60         {
    61             add(j*n+a,j*n+b,c);
    62             add(j*n+b,j*n+a,c);
    63             add((j-1)*n+a,j*n+b,0);
    64             add((j-1)*n+b,j*n+a,0);
    65         }
    66     }
    67     s=1,t=n;
    68     dis();
    69     int ans=d[t];
    70     for (R i=0;i<=k;++i)
    71         ans=min(ans,d[i*n+t]);
    72     cout<<ans;
    73     return 0;
    74 }
    飞行路线

      发现用二维$d$数组更快更好写。

      
     1 # include <cstdio>
     2 # include <iostream>
     3 # include <queue>
     4 # include <cstring>
     5 # define mp make_pair
     6 # define R register int
     7 
     8 using namespace std;
     9 
    10 int h,n,m,k,s,t,a,b,c,firs[10009];
    11 struct edge
    12 {
    13     int co,too,nex;
    14 }g[200009];
    15 int d[10009][12];
    16 bool vis[10009][12];
    17 typedef pair <int,int> pii;
    18 priority_queue <pii,vector<pii>,greater<pii> > q;
    19 
    20 void add(int x,int y,int co)
    21 {
    22     g[++h].too=y;
    23     g[h].co=co;
    24     g[h].nex=firs[x];
    25     firs[x]=h;
    26 }
    27 
    28 void dis()
    29 {
    30     memset(d,127,sizeof(d));
    31     d[s][0]=0;
    32     q.push(mp(0,s));
    33     int beg,j,x;
    34     while (q.size())
    35     {
    36         beg=q.top().second;
    37         q.pop();
    38         x=beg/n;
    39         beg%=n;
    40         if(vis[beg][x]) continue;
    41         vis[beg][x]=true;
    42         for (R i=firs[beg];i;i=g[i].nex)
    43         {
    44             j=g[i].too;
    45             if(d[beg][x]+g[i].co<d[j][x])
    46             {
    47                 d[j][x]=d[beg][x]+g[i].co;
    48                 q.push(mp(d[j][x],j+n*x));
    49             }
    50             if(x==k) continue;
    51             if(d[j][x+1]>d[beg][x])
    52             {
    53                 d[j][x+1]=d[beg][x];
    54                 q.push(mp(d[j][x+1],j+(x+1)*n));
    55             }    
    56         }
    57     }
    58 }
    59 
    60 int main()
    61 {
    62     scanf("%d%d%d",&n,&m,&k);
    63     scanf("%d%d",&s,&t);
    64     for (R i=1;i<=m;++i)
    65     {
    66         scanf("%d%d%d",&a,&b,&c);
    67         add(a,b,c);
    68         add(b,a,c);
    69     }
    70     dis();
    71     printf("%d
    ",d[t][k]);
    72     return 0;
    73 }
    飞行路线_2
     

      改造路:https://www.luogu.org/problemnew/show/P2939

      题意概述:与上一题一模一样。

      
     1 # include <cstdio>
     2 # include <iostream>
     3 # include <queue>
     4 # include <cstring>
     5 # define mp make_pair
     6 # define R register int
     7 
     8 using namespace std;
     9 
    10 int h,n,m,k,s,t,a,b,c,firs[220009];
    11 struct edge
    12 {
    13     int co,too,nex;
    14 }g[4200009];
    15 int d[220009];
    16 bool vis[220009];
    17 typedef pair <int,int> pii;
    18 priority_queue <pii,vector<pii>,greater<pii> > q;
    19 
    20 void add(int x,int y,int co)
    21 {
    22     g[++h].too=y;
    23     g[h].co=co;
    24     g[h].nex=firs[x];
    25     firs[x]=h;
    26 }
    27 
    28 void dis()
    29 {
    30     memset(d,127,sizeof(d));
    31     d[s]=0;
    32     q.push(mp(d[s],s));
    33     int beg,j;
    34     while (q.size())
    35     {
    36         beg=q.top().second;
    37         q.pop();
    38         if(vis[beg]) continue;
    39         vis[beg]=true;
    40         for (R i=firs[beg];i;i=g[i].nex)
    41         {
    42             j=g[i].too;
    43             if(d[beg]+g[i].co>=d[j]) continue;
    44             d[j]=d[beg]+g[i].co;
    45             q.push(mp(d[j],j));
    46         }
    47     }
    48 }
    49 
    50 int main()
    51 {
    52     scanf("%d%d%d",&n,&m,&k);
    53     for (R i=1;i<=m;++i)
    54     {
    55         scanf("%d%d%d",&a,&b,&c);
    56         add(a,b,c);
    57         add(b,a,c);
    58         for (R j=1;j<=k;++j)
    59         {
    60             add(j*n+a,j*n+b,c);
    61             add(j*n+b,j*n+a,c);
    62             add((j-1)*n+a,j*n+b,0);
    63             add((j-1)*n+b,j*n+a,0);
    64         }
    65     }
    66     s=1,t=n;
    67     dis();
    68     int ans=d[t];
    69     for (R i=0;i<=k;++i)
    70         ans=min(ans,d[i*n+t]);
    71     cout<<ans;
    72     return 0;
    73 }
    改造路

      孤岛营救问题:https://www.luogu.org/problemnew/show/P4011

      题意概述:有一个$n imes m$的网格,有些格子之间有门,需要对应的钥匙来打开,有的格子里面有钥匙,必须走到那里才可以拿到,保证钥匙不超过$10$种,求从$(1,1)$走到$(n,m)$的最小步数.

      钥匙当然是可以重复使用的...玩魔塔的后遗症...

      注意到钥匙不超过$10$种是一个很强的条件,可以用来进行状压.用$d[i][j][k]$表示当前在$(i,j)$这个点上,持有的钥匙的状态为$k$的最小步数,把转移条件考虑清楚后直接上堆优化$dijkstra$即可.

      
      1 // luogu-judger-enable-o2
      2 # include <cstdio>
      3 # include <iostream>
      4 # include <cstring>
      5 # include <queue>
      6 # define mp make_pair
      7 # define R register int
      8 
      9 using namespace std;
     10 
     11 const int dx[]={-1,1,0,0};
     12 const int dy[]={0,0,-1,1};
     13 const int maxn=11;
     14 int n,m,p,a,b,c,d,s,k,maxp;
     15 int ans=-1;
     16 int g[maxn][maxn][4];
     17 int dis[maxn][maxn][4250];
     18 int vis[maxn][maxn][4250];
     19 int key[maxn][maxn];
     20 struct z
     21 {
     22     int val,x,y,k;
     23     bool operator > (const z &a) const {
     24         return val>a.val;
     25     }
     26     bool operator < (const z &a) const {
     27         return val<a.val;
     28     }
     29 };
     30 priority_queue <z,vector<z>,greater<z> > q;
     31 
     32 bool mw (int x,int y,int k,int d)
     33 {
     34     int    xx=x+dx[d];
     35     int yy=y+dy[d];
     36     if(xx<=0||xx>n||yy<=0||yy>m) return false;
     37     if((g[x][y][d]&k)==g[x][y][d]) return true;
     38     return false;
     39 }
     40 
     41 void dij()
     42 {
     43     int x,y,k,xx,yy;
     44     z a,b;
     45     a.x=1,a.y=1,a.k=key[1][1],a.val=0;
     46     q.push(a);
     47     while (q.size())
     48     {
     49         a=q.top();
     50         q.pop();
     51         x=a.x;
     52         y=a.y;
     53         k=a.k;
     54         if(vis[x][y][k]) continue;
     55         vis[x][y][k]=true;
     56         a.k|=key[x][y];
     57         if(dis[x][y][a.k]>dis[x][y][k])
     58         {
     59             dis[x][y][a.k]=dis[x][y][k];
     60             q.push(a);
     61             continue;
     62         }
     63         k=a.k;
     64         for (R d=0;d<4;++d)
     65         {    
     66             if(!mw(x,y,k,d)) continue;
     67             xx=x+dx[d];
     68             yy=y+dy[d];
     69             if(dis[xx][yy][k]<=a.val+1) continue;
     70             b.x=xx;
     71             b.y=yy;
     72             b.k=k;
     73             b.val=a.val+1;
     74             dis[xx][yy][k]=a.val+1;
     75             q.push(b); 
     76         }
     77     }
     78 }
     79 
     80 int main()
     81 {
     82     scanf("%d%d%d",&n,&m,&maxp);
     83     memset(dis,1,sizeof(dis));
     84     scanf("%d",&k);
     85     for (R i=1;i<=k;++i)
     86     {
     87         scanf("%d%d%d%d%d",&a,&b,&c,&d,&p);
     88         for (R j=0;j<4;++j)
     89             if(a+dx[j]==c&&b+dy[j]==d) g[a][b][j]|=(1<<p),g[c][d][j^1]|=(1<<p);
     90     }
     91     scanf("%d",&s);
     92     for (R i=1;i<=s;++i)
     93     {
     94         scanf("%d%d%d",&a,&b,&c);
     95         key[a][b]|=(1<<c);
     96     }
     97     dis[1][1][ key[1][1] ]=0;
     98     dij();
     99     ans=-1;
    100     for (R i=0;i<=(1<<(maxp+1));++i)
    101         if(dis[n][m][i]<dis[0][0][0])
    102         {
    103             if(ans==-1) ans=dis[n][m][i];
    104             else ans=min(ans,dis[n][m][i]);
    105         }
    106     printf("%d",ans);
    107     return 0;
    108 }
    孤岛营救问题

      


     

       冻结:https://www.lydsy.com/JudgeOnline/problem.php?id=2662

      题意概述:给定一张$n$个点$m$条边的图,有$k$次机会可以将某条路的长度变为原来的一半,求从$1$到$n$的最小花费.

      首先可以将问题改为每次只可能将下一步要走的边距离改短,因为提早改其实没有什么用处,所以就是一个分层图最短路的板子啦.

      这个插入代码的功能好像...坏了? 

     ---shzr

  • 相关阅读:
    Yet Another Monster Killing Problem
    Element Extermination
    最短路径
    Secret Passwords
    New Year Parties
    Water The Garden
    Zero Quantity Maximization
    Anya and Cubes
    代码规范&《数学之美》读后感
    带负边权的最短路径(有向图)——Bellman-Ford算法
  • 原文地址:https://www.cnblogs.com/shzr/p/9211128.html
Copyright © 2011-2022 走看看