题目链接: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 ",++cas,dis[n]); 88 else 89 printf("Case #%d: -1 ",++cas); 90 } 91 return 0; 92 }