Floyd算法

给定一个带权连通图(有向或无向),要求找出从每个顶点到其他所有顶点之间的距离(最短路径的长度),这就是完全最短路径问题

只要图中不包含长度为负的回路,可用Floyd算法来生成距离矩阵。

该算法既可应用于无向加权图,也可用于有向加权图

对于一个带权有向图:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sqTQSOYQ-1624159776958)(C:\Users\llj\AppData\Roaming\Typora\typora-user-images\image-20210620104500226.png)]

其对应的权重矩阵为:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rSQGXO5x-1624159776963)(C:\Users\llj\AppData\Roaming\Typora\typora-user-images\image-20210620104524204.png)]

而距离矩阵为:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZvA3v7Q8-1624159776967)(C:\Users\llj\AppData\Roaming\Typora\typora-user-images\image-20210620104548550.png)]

而我们的要求便是通过权重矩阵求出其对应的距离矩阵。

算法思想

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mgbTXrsM-1624159776972)(C:\Users\llj\AppData\Roaming\Typora\typora-user-images\image-20210620110026048.png)]

其中,D(0)即为权重矩阵,D(n)即为距离矩阵。

状态转移方程

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OLfphsDu-1624159776976)(C:\Users\llj\AppData\Roaming\Typora\typora-user-images\image-20210620110403324.png)]

由上图可知,第k次迭代中[ i , j ]之间的距离是由d[ i , j ]和d[ i , k ]+d[ k , j ]确定的,哪个距离最短就是哪个,因此我们可以得到如下的状态转移方程:

(C:\Users\llj\AppData\Roaming\Typora\typora-user-images\image-20210620110653280.png)]

如:求出下图的距离矩阵:

[(C:\Users\llj\AppData\Roaming\Typora\typora-user-images\image-20210620110747648.png)]

则用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;
}
  • 1
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

花无凋零之时

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值