算法描述:
简称Floyd算法,是一种基于DP(Dynamic Programming)的最短路径算法,时间复杂度是O(V^3)。
精简版一般形式:
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(dist[i][k]+dist[k][j]<dist[i][j])
dist[i][j]=dist[i][k]+dist[k][j];
}
如果dist[i][k]或者dist[k][j]不存在,程序中用一个很大的数代替。最好写成if(dist[i][k]!=INF&&dist[k][j]!=INF&&dist[i][k]+dist[k][j] < dist[i][j]),从而防止溢出所造成的错误。
重要概念:
1、中间结点:设路径p = < V1,V2,V3…..Vn-1,Vn >,那么V2到Vn-1就叫做路径p的中间结点.
2、最短路径的结构:设结点编号为(1,2,3,4,….,n-1,n), D(i,j,k)表示从vi到vj用了(1,2,3,4….,k-1,k)作为中间结点时的暂时的最短路径(k <= n),显然,当k取n时即D(i,j,n)就是从vi到vj的最短路径(因为当k=n时你考虑了从vi到vj的所有可能路径),因此很自然的运用动态规划,从k=1规划到k=n。
D(i,j,k)有几种特殊情况,D(i,i,k)=0. D(i,j,0) = w(i,j),w(i,j)就是连接vi和vj的弧的权重。
计算D(i,j,k),分两种情况考虑:
1、中间结点用到了k. 此时把该路径分成两段 p1(vi–>k) , p2(k–>vj)
其中p1,p2所用到的中间结点必取自(1,2,3,…k-2,k-1),并且路径长度等于p1+p2
2、中间结点没有用到k. 那么该路径的中间结点必取自(1,2,3,…k-2,k-1)。
综上,得到递归式:
D(i,j,k)= w(i,j) (k=0)
D(i,j,k)= min(D(i,j,k-1), D(i,k,k-1)+D(k,j,k-1)) (k>=1)
算法实现:
//可以实现输出所有节点对的最短距离和任意两节点的最短路径
#include <iostream>
#include <algorithm>
using namespace std;
#define INF 65535
#define N 5
int path[N][N];
void floyd_allPairs(int graph[N][N],int dist[N][N])
{// 矩阵graph[][]存图, 矩阵dist[][]存最短路径
for(int i=0;i<N;i++)
{
for(int j=0;j<N;j++)
{ //初始化距离矩阵dist[][]
dist[i][j]=graph[i][j];
//初始化最短路径
path[i][j]=0;
}
}
for(int k=0;k<N;k++)
{//k为经过的中间节点
for(int i=0;i<N;i++)
{
for(int j=0;j<N;j++)
{
if(dist[i][j]>dist[i][k]+dist[k][j])
{
dist[i][j]=dist[i][k]+dist[k][j];
//将i到j经过的中间节点赋给path!!!!!
path[i][j]=k;
}
}
}
}
}
//用于输出i到j的最短路径
void output(int i,int j){
if(i==j) return;
if(path[i][j]==0) cout<<j<<' ';
else{
output(i,path[i][j]);
output(path[i][j],j);
}
}
int main()
{
int graph[N][N] = {{0,3,8,INF,-4}, {INF,0,INF,1,7},{INF,4,0,INF,INF},
{2,INF,-5,0,INF},{INF,INF,INF,6,0}};
int dist[N][N];
floyd_allPairs(graph,dist);
cout<<"任意两节点间的最短距离值如下:"<<endl;
for(int i=0;i<N;i++)
{
for(int j=0;j<N;j++)
{//输出i到j的最短距离值
cout<<dist[i][j]<<" ";
}
cout<<endl;
}
cout<<"请输入两节点编号[0,N):"<<endl;
int u,v;
while(cin>>u>>v&&(u||v))
{
cout<<"u->v最短路径:"<<endl;
if(dist[u][v]==INF) cout<<"No path"<<endl;
else{
cout<<u<<' ';
output(u,v);
cout<<endl;
}
}
return 0;
}
执行结果:
任意两节点间的最短距离值如下:
0 1 -3 2 -4
3 0 -4 1 -1
7 4 0 5 3
2 -1 -5 0 -2
8 5 1 6 0
请输入两节点编号[0,N):
0 3
u->v最短路径:
0 4 3
0 1
u->v最短路径:
0 4 3 2 1
2 4
u->v最短路径:
2 1 3 4
4 0
u->v最短路径:
4 3 0
0 0
Process returned 0 (0x0) execution time : 472.697 s
Press any key to continue.