POJ 最短路问题集锦:
Dijkstra算法:
详细介绍: http://hi.baidu.com/iotbill/item/cb5fc1de28a3e63721e250d9
Dijkstra算法是典型最短路算法,用于计算一个节点到其他所有节点的最短路径。主要特点是以起始点为中心向外层层扩展,直到扩展到终点为止。Dijkstra算法能得出最短路径的最优解,但由于它遍历计算的节点很多,所以效率低。
POJ 2387 Til the Cows Come Home (problem link adress:http://poj.org/problem?id=2387)
虽然题目不是很好读,但本题是Dijkstra算法的模板式样题!相当于Dijkstra的模板了!
1 #include<iostream> 2 #include<cstring> 3 #include<cstdio> 4 using namespace std; 5 #define Max 0xfffffff 6 #define N 1005 7 int p[N][N],mark[N],f[N]; 8 int t,n; 9 void Dijkstra() 10 { 11 int i,j,k,min; 12 memset(mark,0,sizeof(mark)); 13 for(i=1;i<=n;i++) 14 f[i]=p[1][i]; 15 f[1]=0; 16 for(i=1;i<=n;i++) 17 { 18 min=Max; 19 for(j=1;j<=n;j++) 20 { 21 if(!mark[j]&&f[j]<min) 22 { 23 min=f[j]; 24 k=j; 25 } 26 } 27 if(min==Max) break; 28 mark[k]=1; 29 for(j=1;j<=n;j++) 30 { 31 if(!mark[j]&&f[j]>f[k]+p[k][j]) 32 f[j]=f[k]+p[k][j]; 33 } 34 } 35 printf("%d\n",f[n]); 36 } 37 int main() 38 { 39 int i,j; 40 while(scanf("%d%d",&t,&n)!=EOF) 41 { 42 for(i=1;i<=n;i++) 43 for(j=1;j<=n;j++) 44 p[i][j]=Max; 45 for(j=0;j<t;j++) 46 { 47 int a,b,len; 48 scanf("%d%d%d",&a,&b,&len); 49 if(p[a][b]>len) 50 p[a][b]=p[b][a]=len; 51 } 52 Dijkstra(); 53 } 54 return 0; 55 }
本题中,dijkstra中f【i】保存的是原点到节点i的最短距离。
POJ 2253 Frogger (problem link adress:http://poj.org/problem?id=2253)
第一次读本题的时候,也感觉似懂非懂,由于刷题的顺序是按照别人已经总结好的刷的,所以已经知道别人用Dijkstra解这道题,接着就把题意强扭到了第一块石头到第二块石头的最短距离,结果第二组数据没有通过于是返回认真读题.
前面的那么多东西需要搞懂什么是蛙跳的范围,(最少等于出现的最长蛙跳长度在一个序列中),下面两句话就很重要了。
The frog distance (humans also call it minimax distance) between two stones therefore is defined as the minimum necessary jump range over all possible paths between the two stones.
You are given the coordinates of Freddy's stone, Fiona's stone and all other stones in the lake. Your job is to compute the frog distance between Freddy's and Fiona's stone.
真正的把这两句搞懂,题意就懂了。即让我们求最小的蛙跳范围在所有的可能路径中;
可以从节点1开始遍历,到下一节点(保持两节点间距离最小),直到节点2,在这个过程中记录蛙跳范围。
1 #include<iostream> 2 #include<cstring> 3 #include<cstdio> 4 #include<cmath> 5 using namespace std; 6 #define N 205 7 #define max 10000000.0 8 double p[N][N],f[N]; 9 int mark[N]; 10 int n; 11 int flag; 12 double getlen(double xx1,double yy1,double xx2,double yy2) 13 { 14 return sqrt(1.0*(xx1-xx2)*(xx1-xx2)+1.0*(yy1-yy2)*(yy1-yy2)); 15 } 16 double Dijkstra() 17 { 18 int i,j,k; 19 double min; 20 double temp=0; 21 memset(mark,0,sizeof(mark)); 22 for(i=1;i<=n;i++) 23 f[i]=p[1][i]; 24 f[1]=0;//把1设置为原点 25 for(i=1;i<=n;i++) 26 { 27 min=max; 28 for(j=1;j<=n;j++) 29 { 30 if(!mark[j]&&f[j]<min) 31 { 32 min=f[j]; 33 k=j; 34 } 35 } 36 if(min>temp)//temp记录并保存蛙跳范围 37 temp=min; 38 if(k==2)//如果按每步的最小距离遍历到了节点2,返回蛙跳范围 39 return temp; 40 mark[k]=1; 41 for(j=1;j<=n;j++) 42 { 43 if(!mark[j]) 44 { 45 double data=p[k][j]; 46 if(data<f[j]) 47 f[j]=data; 48 } 49 } 50 51 } 52 53 } 54 int main() 55 { 56 int i,j; flag=1; 57 while(scanf("%d",&n)&&n) 58 { 59 double x[N], y[N]; 60 memset(x,0,sizeof(x)); 61 memset(y,0,sizeof(y)); 62 for(i=1;i<=n;i++) 63 for(j=1;j<=n;j++) 64 p[i][j]=max; 65 for(i=1;i<=n;i++) 66 scanf("%lf%lf",&x[i],&y[i]); 67 for(i=1;i<=n;i++) 68 { 69 for(j=1;j<=n;j++) 70 { 71 if(i==j) continue; 72 double dis=getlen(x[i],y[i],x[j],y[j]); 73 if(p[i][j]>dis) 74 p[i][j]=p[j][i]=dis; 75 } 76 } 77 78 double w=Dijkstra(); 79 printf("Scenario #%d\n",flag++); 80 printf("Frog Distance = %.3lf\n\n",w); 81 } 82 return 0; 83 }
本题中,dijkstra中的f[i]记录的是节点i到与它连接的节点的最短距离。
POJ 1797 Heavy Transport http://poj.org/problem?id=1797
题目大意:有n个路口,每个路口从1到n标记,每两个路口之间有一个限重,问从1到n,最多可以载多重的物体。
本题和上一题的解法类似。注;本题求最大载重,p[i][j]初始化的时候用0或负数初始化,最后每个样例后面要输出一个空格;
实现过程:从节点1开始遍历,寻找与节点1相连的最大权值边,然后把该节点标记,以该节点为中心,然后更新该节点与周围相连节点的权值,这样重复进行;
1 #include<iostream> 2 #include<cstring> 3 #include<cstdio> 4 using namespace std; 5 #define MIN -1 6 #define N 1005 7 int t; 8 int flag; 9 int n,m; 10 int p[N][N],mark[N],f[N]; 11 int Dijkstra() 12 { 13 int i,j,k,temp,min; 14 memset(mark,0,sizeof(mark)); 15 for(i=1;i<=n;i++) 16 f[i]=p[1][i]; 17 f[1]=0;mark[1]=1; 18 temp=1000005; 19 for(i=1;i<=n;i++) 20 { 21 min=-1; 22 for(j=0;j<=n;j++) 23 { 24 if(!mark[j]&&f[j]>min) 25 { 26 min=f[j]; 27 k=j; 28 } 29 30 } 31 if(temp>min) 32 temp=min; 33 if(k==n) 34 return temp; 35 mark[k]=1; 36 for(j=1;j<=n;j++) 37 { 38 if(!mark[j]) 39 { 40 int w=p[k][j]; 41 if(w>f[j]) 42 f[j]=w; 43 } 44 } 45 46 } 47 } 48 int main() 49 { 50 int i,j; 51 scanf("%d",&t); 52 flag=1; 53 while(t--) 54 { 55 scanf("%d%d",&n,&m); 56 for(i=1;i<=n;i++) 57 for(j=0;j<=n;j++) 58 p[i][j]=MIN; 59 for(i=1;i<=m;i++) 60 { 61 int a,b,len; 62 scanf("%d%d%d",&a,&b,&len); 63 if(p[a][b]<len) 64 p[a][b]=p[b][a]=len; 65 } 66 printf("Scenario #%d:\n",flag++); 67 printf("%d\n\n",Dijkstra()); 68 } 69 return 0; 70 }
本题中,dijkstra中的f【j】记录的是节点i到与它连接的节点的最长距离。
上述三题可以知道,根据题目的不同,dijkstra可以做出相应的变化来适合题目。其中,处理最短路的问题时可以用f【i】来保存,原点来目标点的最小距离,处理最大或者最小的边权值时可以用f【i】来保存当前节点与下一节点的最短或最长距离,两者的实现都要通过一个循环来不断的松弛实现。
POJ 3268 Silver Cow Party http://poj.org/problem?id=3268
题意:一群牛(每头牛居住一个的农场,标号从1到n)到第n头牛家里聚会,牛很聪明啊!走的都是最短路,聚会结束,牛各回各家,但是就不能沿着原路返回了,因为是单程的!
问这群牛中谁花费的时间最长?
这样的单向图以前很少做!菜鸟想的简单啊!以为f【i】=p【i】【x】记录的就是 i 牛向x牛奔去最短的距离, b【i】=p【x】【i】记录的就是从x牛的农场各自奔回自己的家中;
于是敲了两次Dijkstra,交上去居然错了! Google一下原来涉及单向图的时候,反过来的时候还是需要转置的,学习了!
WA 代码:
1 #include<iostream> 2 #include<cstring> 3 #include<cstdio> 4 using namespace std; 5 #define N 1005 6 #define max 0xfffffff 7 int n,m,x; 8 int f[N],b[N],mark[N],p[N][N]; 9 void Dijkstra_go() 10 { 11 int i,j,k,min; 12 memset(mark,0,sizeof(mark)); 13 for(i=1;i<=n;i++) 14 f[i]=p[i][x]; 15 f[x]=0; 16 mark[x]=1; 17 for(i=1;i<=n;i++) 18 { 19 min=max; 20 for(j=1;j<=n;j++) 21 { 22 if(!mark[j]&&f[j]<min) 23 { 24 min=f[j]; 25 k=j; 26 } 27 } 28 if(min==max) break; 29 mark[k]=1; 30 for(j=1;j<=n;j++) 31 { 32 if(!mark[j]&&f[j]>f[k]+p[k][j]) 33 f[j]=f[k]+p[k][j]; 34 } 35 } 36 // for(i=1;i<=n;i++) 37 // printf("%d ",f[i]); 38 // printf("\n"); 39 } 40 void Dijkstra_back() 41 { 42 int i,j,k,min; 43 memset(mark,0,sizeof(mark)); 44 for(i=1;i<=n;i++) 45 b[i]=p[x][i]; 46 b[x]=0; 47 mark[x]=1; 48 for(i=1;i<=n;i++) 49 { 50 min=max; 51 for(j=1;j<=n;j++) 52 { 53 if(!mark[j]&&b[j]<min) 54 { 55 min=b[j]; 56 k=j; 57 58 } 59 } 60 if(min==max) break; 61 mark[k]=1; 62 for(j=1;j<=n;j++) 63 { 64 if(!mark[j]&&b[j]>b[k]+p[k][j]) 65 b[j]=b[k]+p[k][j]; 66 67 } 68 } 69 // for(i=1;i<=n;i++) 70 // printf("%d ",b[i]); 71 // printf("\n"); 72 } 73 74 75 76 int main() 77 { 78 int i,j; 79 while(scanf("%d%d%d",&n,&m,&x)!=EOF) 80 { 81 for(i=1;i<=n;i++) 82 for(j=1;j<=n;j++) 83 p[i][j]=max; 84 for(i=1;i<=m;i++) 85 { 86 int a,b,len; 87 scanf("%d%d%d",&a,&b,&len); 88 if(p[a][b]>len) 89 p[a][b]=len; 90 } 91 memset(f,0,sizeof(f)); 92 memset(b,0,sizeof(f)); 93 Dijkstra_go(); 94 Dijkstra_back(); 95 int ans=-1; 96 for(i=1;i<=n;i++) 97 { 98 f[i]+=b[i]; 99 if(f[i]>ans) 100 ans=f[i]; 101 } 102 printf("%d\n",ans); 103 } 104 return 0; 105 }
参考后修改的AC代码:
1 #include<iostream> 2 #include<cstring> 3 #include<cstdio> 4 using namespace std; 5 #define N 1005 6 #define max 0xfffffff 7 int n,m,x; 8 int f[N],b[N],mark[N],p1[N][N],p2[N][N]; 9 void Dijkstra_go() 10 { 11 int i,j,k,min; 12 memset(mark,0,sizeof(mark)); 13 for(i=1;i<=n;i++) 14 f[i]=p1[x][i]; 15 f[x]=0; 16 mark[x]=1; 17 for(i=1;i<=n;i++) 18 { 19 min=max; 20 for(j=1;j<=n;j++) 21 { 22 if(!mark[j]&&f[j]<min) 23 { 24 min=f[j]; 25 k=j; 26 } 27 } 28 if(min==max) break; 29 mark[k]=1; 30 for(j=1;j<=n;j++) 31 { 32 if(!mark[j]&&f[j]>f[k]+p1[k][j]) 33 f[j]=f[k]+p1[k][j]; 34 } 35 } 36 // for(i=1;i<=n;i++) 37 // printf("%d ",f[i]); 38 // printf("\n"); 39 } 40 void Dijkstra_back() 41 { 42 int i,j,k,min; 43 memset(mark,0,sizeof(mark)); 44 for(i=1;i<=n;i++) 45 b[i]=p2[x][i]; 46 b[x]=0; 47 mark[x]=1; 48 for(i=1;i<=n;i++) 49 { 50 min=max; 51 for(j=1;j<=n;j++) 52 { 53 if(!mark[j]&&b[j]<min) 54 { 55 min=b[j]; 56 k=j; 57 58 } 59 } 60 if(min==max) break; 61 mark[k]=1; 62 for(j=1;j<=n;j++) 63 { 64 if(!mark[j]&&b[j]>b[k]+p2[k][j]) 65 b[j]=b[k]+p2[k][j]; 66 67 } 68 } 69 // for(i=1;i<=n;i++) 70 // printf("%d ",b[i]); 71 // printf("\n"); 72 } 73 74 75 76 int main() 77 { 78 int i,j; 79 while(scanf("%d%d%d",&n,&m,&x)!=EOF) 80 { 81 for(i=1;i<=n;i++) 82 for(j=1;j<=n;j++) 83 { 84 p1[i][j]=max; 85 p2[i][j]=max; 86 } 87 for(i=1;i<=m;i++) 88 { 89 int a,b,len; 90 scanf("%d%d%d",&a,&b,&len); 91 if(p1[a][b]>len) 92 { 93 p1[a][b]=len; 94 p2[b][a]=len; 95 } 96 } 97 memset(f,0,sizeof(f)); 98 memset(b,0,sizeof(f)); 99 Dijkstra_go(); 100 Dijkstra_back(); 101 int ans=-1; 102 for(i=1;i<=n;i++) 103 { 104 f[i]+=b[i]; 105 if(f[i]>ans) 106 ans=f[i]; 107 } 108 printf("%d\n",ans); 109 } 110 return 0; 111 }
POJ 1847 Tram http://poj.org/problem?id=1847
汗!本题居然没读懂!英语太菜了!呵呵。。。四级含金量不高啊!搜了篇文章才弄懂题意。
本题最大的挑战就是读题,题目弄清楚了,就ok了! Dijkstra的应用!
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 using namespace std; 5 #define N 105 6 #define max 0xfffffff 7 int f[N],mark[N],p[N][N]; 8 int n,a,b; 9 void Dijkstra() 10 { 11 int i,j,k,min; 12 memset(mark,0,sizeof(mark)); 13 for(i=1;i<=n;i++) 14 f[i]=p[a][i]; 15 f[a]=0; 16 mark[a]=1; 17 for(i=1;i<=n;i++) 18 { 19 min=max; 20 for(j=1;j<=n;j++) 21 { 22 if(!mark[j]&&f[j]<min) 23 { 24 min=f[j]; 25 k=j; 26 } 27 } 28 if(min==max) break; 29 mark[k]=1; 30 for(j=1;j<=n;j++) 31 { 32 if(!mark[j]&&f[k]+p[k][j]<f[j]) 33 f[j]=f[k]+p[k][j]; 34 } 35 } 36 if(f[b]==max) printf("-1\n"); 37 else 38 printf("%d\n",f[b]); 39 } 40 int main() 41 { 42 int i,j; 43 while(scanf("%d%d%d",&n,&a,&b)!=EOF) 44 { 45 int c; 46 for(i=1;i<=n;i++) 47 for(j=1;j<=n;j++) 48 p[i][j]=max; 49 for(i=1;i<=n;i++) 50 { 51 scanf("%d",&c); 52 if(c==0) continue;//第一次的时候忘了考虑c的取值,所以Time Limit Exceeded了一次 53 int d; 54 scanf("%d",&d); 55 p[i][d]=0; 56 c-=1; 57 while(c--) 58 { 59 scanf("%d",&d); 60 p[i][d]=1; 61 } 62 } 63 Dijkstra(); 64 } 65 return 0; 66 }
参考网络资料:http://www.cppblog.com/abilitytao/archive/2009/07/07/89459.html