给定一个带权连通图(有向或无向),要求找出从每个顶点到其他所有顶点之间的距离(最短路径的长度),这就是完全最短路径问题。
只要图中不包含长度为负的回路,可用Floyd算法来生成距离矩阵。
该算法既可应用于无向加权图,也可用于有向加权图。
对于一个带权有向图:
其对应的权重矩阵为:
而距离矩阵为:
而我们的要求便是通过权重矩阵求出其对应的距离矩阵。
算法思想
其中,D(0)即为权重矩阵,D(n)即为距离矩阵。
状态转移方程
由上图可知,第k次迭代中[ i , j ]之间的距离是由d[ i , j ]和d[ i , k ]+d[ k , j ]确定的,哪个距离最短就是哪个,因此我们可以得到如下的状态转移方程:
如:求出下图的距离矩阵:
则用Floyd算法的过程为:
对于D(0),很显然为该图的权重矩阵。
对于D(1),在D(0)的基础上添加了第一行第一列,注意到,由于[b,a]+[a,c]=2+3=5<[b,c],因此就将[b,c]更新成5,以此类推。
对于D(2),在D(1)的基础上添加了第二行第二列,由于[c,b]+[b,a]=7+2=9<[c,a],因此将[c,a]更新成9,以此类推。
……
最终迭代到D(4),算法终止。
伪代码
由上述思想,我们可以得到如下伪代码:
algorithm Floyd(W[1..n, 1..n])
//使用Floyd算法求距离矩阵
//输入:有向图的权重矩阵
//输出:对应的距离矩阵
D <- W
for k <- 1 to n do
for i <- 1 to n do
for j <- 1 to n do
D[i, j] <- min{D[i, j], D[i, k]+D[k, j]}
return D
代码实现
这里我使用的是C++(以前面的例子为例):
首先是权重矩阵:
int W[4][4] = {
{0, 99, 3, 99},
{2, 0, 99, 99},
{99, 7, 0, 1},
{6, 99, 99, 0}
};
然后是Floyd算法的实现:
void Floyd(int W[][4], int D[][4]) {
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 4; j++) {
D[i][j] = W[i][j];
}
}
for (int k = 0; k < 4; k++) {
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 4; j++) {
int temp = D[i][k] + D[k][j];
D[i][j] = (D[i][j] < temp) ? D[i][j] : temp;
}
}
}
}
再在main()函数中启动:
int main() {
int D[4][4];
Floyd(W, D);
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 4; j++) {
cout << D[i][j] << " ";
}cout << endl;
}
return 0;
}
运行结果如下:
可以看到,输出了我们想要的最终结果。
完整代码
#include<iostream>
using namespace std;
int W[4][4] = {
{0, 99, 3, 99},
{2, 0, 99, 99},
{99, 7, 0, 1},
{6, 99, 99, 0}
};
void Floyd(int W[][4], int D[][4]) {
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 4; j++) {
D[i][j] = W[i][j];
}
}
for (int k = 0; k < 4; k++) {
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 4; j++) {
int temp = D[i][k] + D[k][j];
D[i][j] = (D[i][j] < temp) ? D[i][j] : temp;
}
}
}
}
int main() {
int D[4][4];
Floyd(W, D);
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 4; j++) {
cout << D[i][j] << " ";
}cout << endl;
}
return 0;
}