求一个图的最短路径。
一、多源最短路
1.Floyed-Wallsahll算法
借用动态规划的思想,是枚举中转点,其中核心语句的原型是mp[i][k][k-1]+mp[k][j][k-1]<=mp[i][j][k],只不过因为最后的k,k-1没什么用,就压缩成了二维。
代码:
1 for(int k=1;k<=n;k++)//k循环一定要在外面 2 { 3 for(int i=1;i<=n;i++) 4 { 5 for(int j=1;j<=n;j++) 6 { 7 if(mp[i][j]>mp[i][k]+mp[k][j]) 8 { 9 mp[i][j]=mp[i][k]+mp[k][j];//核心算法语句 10 } 11 } 12 } 13 }
借助Floyed算法,加入path[][]数组,可以进行路径的保存,最终实现路径输出。代码如下:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<iostream> 2 #include<cstdio> 3 using namespace std; 4 const int maxn=1000; 5 const int inf=0x3f3f3f3f; 6 int path[maxn][maxn]; 7 int mp[maxn][maxn]; 8 int n,m; 9 void Floyed()//Floyed算法核心语句 10 { 11 int i,j,k; 12 for(i=1;i<=n;i++) 13 { 14 for(j=1;j<=n;j++) 15 { 16 if(mp[i][j]<inf&&i!=j) 17 path[i][j]=j;//记录能到达的路径 18 else 19 path[i][j]=-1; 20 } 21 } 22 for(k=1;k<=n;k++) 23 { 24 for(i=1;i<=n;i++) 25 { 26 for(j=1;j<=n;j++) 27 { 28 if(mp[i][j]>mp[i][k]+mp[k][j]) 29 { 30 mp[i][j]=mp[i][k]+mp[k][j]; 31 path[i][j]=path[i][k];//更新路径 32 } 33 } 34 } 35 } 36 } 37 void Output(int start,int end)//最短路输出 38 { 39 int next=start; 40 printf("%d",start); 41 while(next!=end) 42 { 43 printf("-->"); 44 printf("%d",path[next][end]); 45 next=path[next][end]; 46 } 47 cout<<endl; 48 } 49 int main() 50 { 51 cin>>n>>m; 52 for(int i=1;i<=n;i++) 53 { 54 for(int j=1;j<=n;j++) 55 { 56 if(i==j) 57 mp[i][j]=0; 58 else 59 mp[i][j]=inf; 60 } 61 } 62 int a,b,c; 63 for(int i=1;i<=m;i++) 64 { 65 scanf("%d%d%d",&a,&b,&c); 66 mp[a][b]=c; 67 mp[b][a]=c; 68 } 69 Floyed(); 70 int Qa,Qb; 71 while(cin>>Qa>>Qb) 72 { 73 cout<<mp[Qa][Qb]<<endl; 74 Output(Qa,Qb); 75 } 76 return 0; 77 }
二、单源最短路
1.Dijkstra算法
借助贪心算法的思想。
主要用来计算一个节点到其他所有点的最短路径。以起点为中心点向外拓展。
该算法的要求:图中不存在负权边。
其中典型的操作松弛。首先用dis[]数组记录源点到其他所有点的距离并标记正无穷,然后从源点开始,比如dis[3]表示1到3的距离,此时比较dis[3]和dis[2]+mp[2][3],即如果此时1到3的距离通过点2松弛如果更小,那么更新,其他依次操作,直到所有点都更新完成。
代码:
1 int book[50],min,u,v,i,j; 2 for(i=1;i<=n;i++)//把距离改为源点到所有可直接连通点的距离 3 { 4 dis[i]=map[1][i]; 5 } 6 for(i=1;i<=n;i++)//标记 7 { 8 book[i]=0; 9 } 10 book[1]=1;//1已经遍历过 11 for(i=1;i<=n-1;i++)//进行n-1次操作 12 {//操作是寻找距离1号最近的顶点 13 min=din; 14 for(j=1;j<=n;j++)//寻找可以更新的点 15 { 16 if(book[j]==0&&dis[j]<min) 17 { 18 min=dis[j]; 19 u=j; 20 } 21 } 22 book[u]=1; 23 for(v=1;v<=n;v++) 24 { 25 if(map[u][v]<din) 26 { 27 if(dis[v]>dis[u]+map[u][v]) 28 { 29 dis[v]=dis[u]+map[u][v]; 30 } 31 } 32 } 33 }
2.Bellman_Ford算法
采用“松弛”的策略,从原点到每个点的方案作对比,逐步更新,更新n(顶点)-1次后,一定全部更新完毕,再次判断是否可更新,如果还可更新,则一定存在负权回路。
代码如下:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<iostream> 2 #include<cstdio> 3 using namespace std; 4 const int inf=0x3f3f3f3f; 5 const int maxn=1e5; 6 struct Edge 7 { 8 int u,v,w; 9 }; 10 Edge e[maxn]; 11 int dis[maxn],pre[maxn]; 12 int n,m,s;//n为点数,m为通路条数,s为源点 13 14 bool Bellman_Ford() 15 { 16 for(int i=1;i<=n;i++)//初始化源点到其他各个点的距离 17 { 18 if(i==s) 19 dis[i]=0; 20 else 21 dis[i]=inf; 22 } 23 for(int i=1;i<=n-1;i++)//核心代码,“松弛”操作 24 { 25 for(int j=1;j<=m;j++) 26 { 27 if(dis[e[j].v]>dis[e[j].u]+e[j].w) 28 { 29 dis[e[j].v]=dis[e[j].u]+e[j].w; 30 pre[e[j].v]=e[j].u;//前驱路线更新 31 } 32 } 33 } 34 bool flag=true;//判断是否含有负权回路的标志 35 for(int i=1;i<=m;i++) 36 { 37 if(dis[e[i].v]>dis[e[i].u]+e[i].w) 38 { 39 flag=false; 40 break; 41 } 42 } 43 return flag; 44 } 45 void Output_path(int s)//路径输出的函数 46 { 47 while(s!=pre[s]) 48 { 49 printf("%d",s); 50 printf("-->"); 51 s=pre[s]; 52 } 53 if(s==pre[s]) 54 cout<<s<<endl; 55 } 56 int main() 57 { 58 cin>>n>>m; 59 cin>>s; 60 for(int i=1;i<=n;i++) 61 { 62 scanf("%d%d%d",&e[i].u,&e[i].v,&e[i].w); 63 } 64 pre[s]=s; 65 bool flag; 66 flag=Bellman_Ford(); 67 if(flag) 68 { 69 for(int i=1;i<=n;i++) 70 { 71 printf("%d ",dis[i]); 72 Output_path(i); 73 } 74 } 75 else 76 printf("图中含有负权回路。 "); 77 return 0; 78 }