HDU 4725 The Shortest Path in Nya Graph(spfa+虚拟点建图)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4725

题目大意:有n层,n个点分布在这些层上,相邻层的点是可以联通的且距离为c,还有额外给出了m个条边,求1号点到n号点的最短距离,若无法到达则输出“-1”。

解题思路:最短路问题,主要是建图很难。如果按常规建法,用邻接表存每层的节点编号然后在建边肯定会超时,因为如果点只分布在两个层上,那建边的复杂度就是O(n^2)了。所以要改变一下思路,可以用n个虚拟点来代表n层,把连到该层的点都连接到虚拟点上,同一层花费为0,不同层花费为c。但是还需要一点处理,不然这样的话同一层的点的花费就会变成0,原本可能不可达的变得可达。这是我从别人博客看来的两种方法(出处):

A.每层拆两个点,一个点管入,一个点管出,这样的话同层的点不会回到同层的另外一个点上。

B.每层拆一个点,这个点只管入,而处于该层的点则向左右两层虚拟点相连。

我用的是方法B,建了2n个点(有n个是虚拟点),边数大概为5n条(点和点2n,点和相邻层2n,虚拟点到同层的点n)

代码:

 1 #include<iostream>
 2 #include<queue>
 3 #include<cstring>
 4 #include<algorithm>
 5 using namespace std;
 6 const int N=2e5+5;
 7 const int INF=0x3f3f3f3f;
 8 
 9 struct node{
10     int to,next,w;
11 }edge[5*N];
12 
13 int n;
14 int idx,head[N],dis[N],layer[N];//layer[i]记录第i个点所在的层 
15 bool vis[N],sign[N];//sign[i]记录第i层是否有点
16 
17 void init(){
18     idx=1;
19     memset(head,-1,sizeof(head));
20 }
21 
22 void addEdge(int u,int v,int w){
23     edge[idx].to=v;
24     edge[idx].w=w;
25     edge[idx].next=head[u];
26     head[u]=idx; 
27     idx++;
28 }
29 
30 void spfa(int s){
31     memset(dis,0x3f,sizeof(dis));
32     memset(vis,false,sizeof(vis));
33     dis[s]=0;
34     queue<int>q;
35     q.push(s);
36     while(!q.empty()){
37         int k=q.front();
38         q.pop();
39         vis[k]=false;
40         for(int i=head[k];i!=-1;i=edge[i].next){
41             node t=edge[i];
42             if(dis[k]+t.w<dis[t.to]){
43                 dis[t.to]=dis[k]+t.w;
44                 if(!vis[t.to]){
45                     q.push(t.to);
46                     vis[t.to]=true;
47                 }
48             }
49         }
50     }
51 }
52 
53 int main(){
54     int t,cas=0;
55     scanf("%d",&t);
56     while(t--){
57         init();
58         memset(sign,false,sizeof(sign));
59         int m,c;
60         scanf("%d%d%d",&n,&m,&c);
61         for(int i=1;i<=n;i++){
62             scanf("%d",&layer[i]);
63             sign[layer[i]]=true;
64         }
65         for(int i=1;i<=n-1;i++){        //相邻层的虚拟点建边
66             if(sign[i]&&sign[i+1]){        //当两个相邻层都有点才建边 
67                 addEdge(i+n,i+n+1,c);
68                 addEdge(i+n+1,i+n,c);
69             } 
70         }
71         for(int i=1;i<=n;i++){            //虚拟点到同一层的点建边 
72             addEdge(layer[i]+n,i,0);     
73             if(layer[i]>1)                //点和相邻层的虚拟点建边 
74                 addEdge(i,layer[i]+n-1,c);
75             if(layer[i]<n)
76                 addEdge(i,layer[i]+n+1,c);
77         }
78         
79         for(int i=1;i<=m;i++){
80             int u,v,w;
81             scanf("%d%d%d",&u,&v,&w);
82             addEdge(u,v,w);
83             addEdge(v,u,w);
84         }
85         spfa(1);    
86         if(dis[n]<INF)
87             printf("Case #%d: %d\n",++cas,dis[n]);
88         else
89             printf("Case #%d: -1\n",++cas);
90     }
91     return 0;
92 }

 

posted @ 2017-11-23 00:02  Yeader  阅读(175)  评论(0编辑  收藏  举报