Floyd算法是求解任意两点之间的最短路的算法(多源最短路问题)。它的代码长度很短,只有五行,可是算法复杂度很高,可以在O(|n^3|)时间里求得所有两点间的最短路长度,所以对于三位数一下的最短路题目,可以考虑使用Floyd算法,简单实用。Floyd也可以处理负数的情况,而且可以判断图中是否有负图。
小哼准备去几个城市去旅游,有些城市之间有公路,有些城市之间没有,如下图,为了节省花费以及方便计划旅程,小哼希望出发之前知道两个城市之间的最短路径。
上图中有4个城市,八条公路,公路上的数字表示这条公路的长短,公路都是单向的。现在我们需要求任意两个城市之间的最短路径,也就是求任意两点之间的最短路。
我们现在用一个4*4的矩阵(二维数组e)来存图,比如1号城市到2号城市的路程为2,则设e[1][2]的值为2。2号城市无法到达4号城市,则设置e[2][4]的值为inf,另外一个城市自己到自己的路程为0,即e[i][i] = 0。
城市 | 1 | 2 | 3 | 4 |
1 | 0 | 2 | 6 | 4 |
2 | inf | 0 | 3 | inf |
3 | 7 | inf | 0 | 1 |
4 | 5 | inf | 12 | 0 |
当任意两点之间不允许经过第三个点时,这些城市之间的最短路程就是初始路程。如下图所示:
城市 | 1 | 2 | 3 | 4 |
1 | 0 | 2 | 6 | 4 |
2 | inf | 0 | 3 | inf |
3 | 7 | inf | 0 | 1 |
4 | 5 | inf | 12 | 0 |
假如现在只允许经过1号顶点,求任意两个点之间的最短路程。只需要判断e[i][1] + e[1][j]是否比e[i][j]小即可。e[i][j]表示的是从i号顶点到j号顶点之间的路程。e[i][1]+e[1][j]表示的是从i号顶点先到1号顶点,再从1号顶点到j号顶点的路程之和。代码实现如下
for(int i = 1; i <= n; i++) {
for(int j = 1; j <= n; j++) {
if(e[i][j] > e[i][1] + e[1][j])
e[i][j] = e[i][1] + e[1][j];
}
}
如果只允许经过1号顶点的情况下,任意两点之间的最短路程为:
城市 | 1 | 2 | 3 | 4 |
1 | 0 | 2 | 6 | 4 |
2 | inf | 0 | 3 | inf |
3 | 7 | 9 | 0 | 1 |
4 | 5 | 7 | 11 | 0 |
通过上图我们发现:在只通过1号顶点中转的情况下,3号顶点到2号顶点(e[3][2]),4号顶点到2号顶点(e[4][2])以及4号顶点到3号顶点(e[4][3])的路程都变短了。
接下来继续求在只允许经过1和2号两个顶点的情况下任意两点之间的最短路径。然后我们只允许经过1号顶点时任意两点的最短路程的结果下,再判断如果经过2号顶点是否可以使得i号顶点到j号顶点之间的路程变短,即同上我们只需要比较e[i][j]和e[i][2]+e[2][j],代码如下:
for(int i = 1; i <= n; i++) {
for(int j = 1; j <= n; j++) {
if(e[i][j] > e[i][2] + e[2][j])
e[i][j] = e[i][2] + e[2][j];
}
}
在只允许经过1和2号顶点的情况下,任意两点之间的最短路程更新为:
城市 | 1 | 2 | 3 | 4 |
1 | 0 | 2 | 5 | 4 |
2 | inf | 0 | 3 | inf |
3 | 7 | 9 | 0 | 1 |
4 | 5 | 7 | 10 | 0 |
通过上图得知,在相比只允许通过1号顶点进行中转的情况下,这里允许通过1和2号顶点进行中转,使得e[1][3]和e[4][3]的路程变得更短
同理,继续在只允许经过1,2,3号顶点进行中转的情况下,求任意两点之间的最短路程。任意两点之间的最短路程更新为:
城市 | 1 | 2 | 3 | 4 |
1 | 0 | 2 | 5 | 4 |
2 | 10 | 0 | 3 | 4 |
3 | 7 | 9 | 0 | 1 |
4 | 5 | 7 | 10 | 0 |
最后允许通过所有顶点作为中转,任意两点之间最终的最短路程为:
城市 | 1 | 2 | 3 | 4 |
1 | 0 | 2 | 5 | 4 |
2 | 9 | 0 | 3 | 4 |
3 | 6 | 8 | 0 | 1 |
4 | 5 | 7 | 10 | 0 |
核心代码只有五行:
for(int k = 1; k <= n; k++) {
for(int i = 1; i <= n; i++) {
for(int 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 <stdio.h>
#define inf 0x3f3f3f3f
int e[105][105];
int main() {
int n, m;//代表n个点,m条边
int t1, t2;//求t1到t2的距离
int u, v, w;
scanf("%d %d", &n, &m);
scanf("%d %d", &t1, &t2);
//初始化
for(int i = 1; i <= n; i++) {
for(int j = 1; j <= n; j++) {
if(i == j) e[i][j] = 0;
else e[i][j] = inf;
}
}
//读入边
for(int i = 1; i <= m; i++) {
scanf("%d %d %d", &u, &v, &w);
e[u][v] = w;//单向边,如果双向就再加上:e[v][u]=w;
}
//Floyd-Warshall核心算法
for(int k = 1; k <= n; k++) {
for(int i = 1; i <= n; i++) {
for(int j = 1; j <= n; j++) {
if(e[i][j] > e[i][k] + e[k][j])
e[i][j] = e[i][k] + e[k][j];
}
}
}
//输出t1到t2的最短距离
printf("%d\n", e[t1][t2]);
return 0;
}
以上内容大多参考《啊哈算法》。