我们可以通过Floyd-Warshall 算法求出任意两点之间的最短路径。它的时间复杂度是O(N3)。
令人震撼的是它只有五行代码,实现起来很容易。正是因为实现非常容易,如果时间复杂度要求不高,
使用Floyd-Warshall 来求指定两点之间的最短路径和指定一个点到其余各个顶点的最短路径也是可行的。
当然也有更快的算法,这里暂时只讨论Floyd-Warshall 算法。
核心代码只有五行:
//e[i][j]表示i->j的权值,不存在时设为INF 但是e[i][i]设为0
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号顶点进行中转,接下来只允许经过1和2号顶点进行中转……允许经过1~n号所有顶点进行中转,求任意两点之间的最短路程。
用一句话概括就是:从i号顶点到j号顶点只经过前k号点的最短路程。
其实这是一种动态规划的思想。
设图G中n 个顶点的编号为1到n。
令c [i, j, k]表示从i 到j 的最短路径的长度,其中k 表示该路径中的最大顶点,也就是说c[i,j,k]这条最短路径所通过的中间顶点最大不超过k。
因此,如果G中包含边< i, j>,则c[i, j, 0] =边< i, j> 的长度;若i= j ,则c[i,j,0]=0;如果G中不包含边< i, j>,则c (i, j, 0)= +∞。c[i, j, n] 则是从i 到j 的最短路径的长度。
对于任意的k>0,通过分析可以得到:中间顶点不超过k 的i 到j 的最短路径有两种可能:
若不含,则该路径长度应为c[i, j, k-1],
否则长度为 c[i, k, k-1] +c [k, j, k-1]。c[i, j, k]可取两者中的最小值。
状态转移方程:c[i, j, k]=min{c[i, j, k-1], c [i, k, k-1]+c [k, j, k-1]},k>0。
练习:
hdu 2544 最短路 http://acm.hdu.edu.cn/showproblem.php?pid=2544
看了下数据量不够大,Floyd可以水过。 就用它当练习了。
#include <cstdio>
#include <algorithm>
#define inf 0x3f3f3f3f
using namespace std;
int main()
{
int N,M;
int i,j,k;
int t1,t2,t3;
int e[105][105];
while(scanf("%d%d",&N,&M)==2 && N && M)
{
//初始化
for(i=1;i<=N;i++)
for(j=1;j<=N;j++)
if(i==j) e[i][j]=0;
else e[i][j]=inf;
//读入边
for(i=1;i<=M;i++)
{
scanf("%d %d %d",&t1,&t2,&t3);
e[t1][t2]=e[t2][t1]=t3;
}
//floyd-warshall
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];
printf("%d\n",e[1][N]);
}
return 0;
}