定义
弗洛伊德最短距离算法(Floyd Shortest Path Algorithm)又称为插点法,是一种利用动态规划的思想寻找给定的加权图中多源点之间最短路径的算法。
原理
我们的目标是寻找从点 i 到点 j 的最短路径。
从任意节点 i 到任意节点 j 的最短路径不外乎2种可能。
①直接从 i 到 j。
②从 i 经过若干个节点 k 到 j。
令 Dis(i,j) 为节点 i 到节点 j 的最短路径的距离。
对于每一个节点k,我们检查 Dis(i,k) + Dis(k,j) < Dis(i,j) 是否成立,如果成立,则Dis(i,j) = Dis(i,k) + Dis(k,j)。
所以,当我们遍历完所有节点 k,Dis(i,j)中记录的便是 i 到 j 的最短路径的距离。
❗弗洛伊德不能处理负权图。
若求 1->3 的最短路,此情况则会陷入1->2->3->1->2->3->1->2->3......死循环。
实现
#include<bits/stdc++.h> using namespace std; const int maxn=1005; const int inf=0x3f3f3f3f; int n,m,e[maxn][maxn]; void floyd(); int main() { int i,u,v,w; fill(e[0],e[0]+maxn*maxn,inf); for(i=0;i<maxn;i++) e[i][i]=0; scanf("%d%d",&n,&m); for(i=1;i<=m;i++) { scanf("%d%d%d",&u,&v,&w); e[u][v]=w; } floyd(); system("pause"); return 0; } void floyd() { int i,j,k; for(k=1;k<=n;k++) for(i=1;i<=n;i++) for(j=1;j<=n;j++) if(e[i][j]>e[i][k]+e[k][j]) e[i][j]=e[i][k]+e[k][j]; }
扩展
记录路径
代码1
#include<bits/stdc++.h> using namespace std; const int maxn=1005; const int inf=0x3f3f3f3f; int n,m,e[maxn][maxn],path[maxn][maxn],ans[maxn],top=-1; void floyd(); void print(int x,int y); int main() { int i,u,v,w; fill(e[0],e[0]+maxn*maxn,inf); for(i=0;i<maxn;i++) e[i][i]=0; scanf("%d%d",&n,&m); for(i=1;i<=m;i++) { scanf("%d%d%d",&u,&v,&w); e[u][v]=w; } floyd(); scanf("%d%d",&u,&v); ans[++top]=u; print(u,v); ans[++top]=v; for(i=0;i<=top;i++) printf("%d%s",ans[i],i==top?"\n":"->"); system("pause"); return 0; } void print(int x,int y) { if(!path[x][y]) return; print(x,path[x][y]); ans[++top]=path[x][y]; print(path[x][y],y); } void floyd() { int i,j,k; for(k=1;k<=n;k++) for(i=1;i<=n;i++) for(j=1;j<=n;j++) if(e[i][j]>e[i][k]+e[k][j]) { e[i][j]=e[i][k]+e[k][j]; path[i][j]=k; } }
代码2
#include<bits/stdc++.h> using namespace std; const int maxn=1005; const int inf=0x3f3f3f3f; int n,m,e[maxn][maxn],path[maxn][maxn]; void floyd(); void print(int x,int y); int main() { int i,u,v,w; fill(e[0],e[0]+maxn*maxn,inf); for(i=0;i<maxn;i++) e[i][i]=0; for(i=0;i<maxn;i++) for(int j=0;j<maxn;j++) path[i][j]=j; scanf("%d%d",&n,&m); for(i=1;i<=m;i++) { scanf("%d%d%d",&u,&v,&w); e[u][v]=w; } floyd(); scanf("%d%d",&u,&v); print(u,v); system("pause"); return 0; } void print(int x,int y) { if(x==y) { printf("%d",y); return ; } int k=path[x][y]; printf("%d-->",x); print(k,y); } void floyd() { int i,j,k; for(k=1;k<=n;k++) for(i=1;i<=n;i++) for(j=1;j<=n;j++) if(e[i][j]>e[i][k]+e[k][j]) { e[i][j]=e[i][k]+e[k][j]; path[i][j]=path[i][k]; } }
找环
有向图和无向图的环是不同的,有向图中至少2个就可以成环,而无向图中至少需要3个顶点才能成环。
有向图
#include<bits/stdc++.h> using namespace std; const int maxn=1005; const int inf=0x3f3f3f3f; int n,m,e[maxn][maxn]; void floyd(); int main() { int i,u,v,w,ans=inf; fill(e[0],e[0]+maxn*maxn,inf); scanf("%d%d",&n,&m); for(i=1;i<=m;i++) { scanf("%d%d%d",&u,&v,&w); e[u][v]=w; } floyd(); for(i=1;i<=n;i++) ans=min(ans,e[i][i]); printf("%d\n",ans); system("pause"); return 0; } void floyd() { int i,j,k; for(k=1;k<=n;k++) for(i=1;i<=n;i++) for(j=1;j<=n;j++) if(e[i][j]>e[i][k]+e[k][j]) e[i][j]=e[i][k]+e[k][j]; }
无向图
#include<bits/stdc++.h> using namespace std; const int maxn=1005; const int inf=999999; int n,m,e[maxn][maxn],ve[maxn][maxn]; void floyd(); int main() { int i,u,v,w; fill(e[0],e[0]+maxn*maxn,inf); fill(ve[0],ve[0]+maxn*maxn,inf); for(i=0;i<maxn;i++) e[i][i]=ve[i][i]=0; scanf("%d%d",&n,&m); for(i=1;i<=m;i++) { scanf("%d%d%d",&u,&v,&w); ve[u][v]=e[u][v]=w; ve[v][u]=e[v][u]=w; } floyd(); system("pause"); return 0; } void floyd() { int MinCost=inf; for(int k=1;k<=n;k++) { for(int i=1;i<k;i++) //更新k点之前枚举ij求经过ijk的最小环 for(int j=i+1;j<k;j++) MinCost=min(MinCost,e[i][j]+ve[i][k]+ve[k][j]); for(int i=1;i<=n;i++) //更新k点 for(int j=1;j<=n;j++) e[i][j]=min(e[i][j],e[i][k]+e[k][j]); } if(MinCost==inf) puts("It's impossible."); else printf("%d\n",MinCost); }