最短路径Floyd算法图解与C++实现

邻接矩阵特征 

Floyd算法建立在对图的邻接矩阵的操作上,理解Floyd首先要理解邻接矩阵

对于有向图,邻接矩阵的一行代表该顶点的出边,一列代表该顶点的入边

对于无向图则不区分出边与入边,邻接矩阵表示成一个对称矩阵

graph[i][j] 代表 i -> j ,由 i 直接到 j 的代价,graph[i][k] + graph[k][j]就表示 (i -> k) + (k -> j) ,即 i -> k -> j ,由 i 经 k 到 j 的代价

Floyd代码中有三层for循环,其中最内层for就是在操作这样一个值 + 一行值

下面以上图为例,分析Floyd算法运行过程


 Floyd算法

Floyd算法中使用一个三维数组 ans[k][i][j],表示可以经过的中间结点序号小于等于 k 时,顶点 i 到顶点 j 的最小代价

① ans[0][i][j]的值即邻接矩阵

② ans[1][i][j]的值即,可以经过顶点 1 ,各顶点对间最小代价,这个值是在ans[0][i][j]的基础上得到的。

    经过顶点 1 有什么好处?

    以前只有 i -> j 的边能用,现在 i -> 1 -> j 的边也能用了,表现在邻接矩阵上就是,以前的代价是graph[i][j],现在可以是

graph[i][1]  + graph[1][j]了,即在这个三维矩阵中ans[1][i][j]的值应该等于min{ans[0][i][j], ans[0][i][1] + ans[0][1][j]},经过这样的比较,就将顶点 1 能产生的影响考虑了进去。

③ 同上,将所有顶点都考虑进去,就得到了整个图中顶点对的最短路径


代码实现与分析

代码中实际使用的是一个二维数组ans[i][j],下面先给出代码,然后在解释代码中分析使用二维而不是三维的可行性

#include <iostream>

using namespace std;
#define MAXN 110
int ans[MAXN][MAXN];

int main() {
	int n, m; //n个顶点,序号为 1 到 n, m条边
	while (cin >> n >> m) {
		for (int i = 1; i <= n; i++) {
			for (int j = 1; j <= n; j++) {
				ans[i][j] = -1; //不可达
			}
			ans[i][i] = 0;
		}
		for (int i = 0; i < m; i++) { //顶点u到顶点v,代价为w
			int u, v, w;
			cin >> u >> v >> w; //顶点从1开始编号
			ans[u][v] = w;
			ans[v][u] = w;
		}
		for (int k = 1; k <= n; k++) {
	            for (int i = 1; i <= n; i++) {
		        for (int j = 1; j <= n; j++) {
			        if (ans[i][k] == -1 || ans[k][j] == -1)
				        continue;
			        if (ans[i][j] == -1 || ans[i][j] < ans[i][k] + ans[k][j])
				        ans[i][j] = ans[i][k] + ans[k][j];
		        }
	            }
                }
	 }

}

代码的核心就是三层for循环,三个for各有各的作用。首先要明确的是,三层for的最终目的就是要求出ans[n][i][j],ans[n][i][j]是将所有顶点能产生的影响都考虑进去时,各顶点对的最短路径。

①最内层for,k不变,i不变,j遍历所有顶点

                                                             

 

    比如k = 1,i = 3,j = 1...4,实现的是修改ans[3][j],顶点 3 到各顶点最小代价,即第三行值,考虑的是顶点k = 1的影响,顶点k = 1能对顶点 3 产生的影响就是,从顶点 3 走向顶点 1 ,再从顶点1去各顶点,这个代价与原来不考虑1的哪个小,就是

ans[3][j] = min(ans[3][1] + ans[1][j], ans[3][j]),从图上来看,3->1,应该找3行1列,再1->*,应该找1行*列,将这两步代价相加即3->*代价,图上是深蓝方块去加浅蓝方块,再与对应黄色方块比较,并修改黄色方块

最内层for修改的是,考虑需要小于k的顶点时,矩阵第i行的值,是顶点 i 考虑顶点k后的结果

②中间层for,j 遍历各个顶点,每个顶点都考虑一次顶点k产生的影响,就是顶点k对整个图产生的影响

③最外层for,k 遍历各个顶点,整个矩阵对每个顶点的影响都考虑一次,就是所有顶点对整个图产生的影响,即最终所求

二维替代三维

现在再来说明为什么能用二维数组替代三维数组

                                              

最外层for每轮要将这个二维数组所有元素修改一遍,从代码中可以看出,用来比较的,即需要其值不被随意改动的是一行一列的元素,修改其他9个白色方块时自然不会对这7个蓝色方块造成影响,而修改蓝色方块时呢?

①深蓝:即j = k,ans[i][k] = min(ans[i][k] + ans[k][k], ans[i][k]) ,ans[k][k]是自己到自己一定为0

②浅蓝:即i = k,ans[k][j] = min(ans[k][k] + ans[k][j], ans[k][j]),ans[k][k]同样为0

可以看出两种情况都不会造成影响,蓝色方块在这一轮k中是固定不变的,从算法上理解就是要考虑的中间顶点是k,如果k是两端结点,那么某个顶点经过k再到k,这个值就是这个顶点直接到k的值,另一端同理,从k经k到某个顶点=从k到某个顶点

既然没影响,自然可以在原地修改数组中的元素值


这里需要特别注意的是,最外层for每轮计算一个顶点能产生的影响,这个影响是该顶点在当前状态下能产生的直接影响,也就是顶点1能产生的影响,并不是在计算过k=1之后就固定了,只是在算法运行过程中,先把顶点1的影响加入,在后续过程中持续发挥着作用

比如,

k = 0时只将graph[i][j]考虑进去,

k = 1时将graph[i][j],graph[i][1] + graph[1][j]考虑进去

k = 2时将graph[i][j],graph[i][1] + graph[1][j],graph[i][1] + graph[1][2] + graph[2][j],graph[i][2] + graph[2][j]考虑进去,此时顶点1的影响不仅局限于k = 1那一轮计算过的graph[i][1] + graph[1][j],还有graph[i][1] + graph[1][2] + graph[2][j],先经1再经2

k = 2时看似是比较了两个值,其实是将四种路径进行比较取最小值,这样到最后一轮,就是所有路径的最小值

<think>好的,我需要帮助用户找到Floyd算法图解教程详细过程。首先,我得回忆一下Floyd算法的基本概念和工作原理。记得Floyd算法是用来解决多源最短路径问题的,基于动态规划,通过三重循环更新距离矩阵。用户想要的是图解说明,所以应该用步骤分解的方式,结合矩阵的变化来展示过程。 接下来,我需要确保符合用户提供的系统级指令。所有数学表达式要用$...$,比如距离矩阵的更新公式。独立公式需要用$$...$$,比如状态转移方程。同时,要正确使用LaTeX语法,避免错误。 用户提供的引用资料提到Floyd算法的动态规划思想、时间复杂度O(n³)以及图解证明中的问题。需要整合这些信息,特别是引用3中的图解C++实现,以及引用4中关于图解证明的讨论。要确保引用的内容自然地融入回答,并在末尾添加引用标识。 然后,按照用户示例的结构,先解释Floyd算法的步骤,再给出伪代码和示例,最后生成相关问题。需要分步骤详细说明算法如何初始化距离矩阵,如何通过中间点k更新最短路径,并配合矩阵变化的例子。例如,一个3个顶点的图,展示每一步的矩阵变化。 还要注意回答的中文表达,结构清晰,逐步引导用户理解。可能需要绘制一个简单的图例,用文字描述顶点和边的权重,然后逐步应用Floyd算法,列出每一步的中间矩阵,并用文字解释每个步骤的变化原因。 此外,用户可能对算法的时间复杂度和空间复杂度感兴趣,需要提及这些内容,并引用相关资料。同时,注意引用中提到的证明问题,即点集排序的影响,可能需要简要说明,但重点放在图解过程上。 最后,生成的相关问题要围绕Floyd算法的应用、其他算法的比较、优化方法等,确保问题有针对性,帮助用户进一步学习。</think>### Floyd算法图解教程详细过程 Floyd算法基于动态规划思想,通过逐步更新距离矩阵求解所有顶点对的最短路径。以下通过一个具体示例分步骤说明: #### 示例图结构 假设有3个顶点(A、B、C)构成的图,邻接矩阵表示为: $$ D^{(0)} = \begin{bmatrix} 0 & 4 & ∞ \\ 6 & 0 & 2 \\ 3 & ∞ & 0 \\ \end{bmatrix} $$ (∞表示不可达) #### 算法步骤分解 **1. 初始化距离矩阵** 初始距离矩阵$D^{(0)}$直接存储边的权值,对角线元素为0,不可达顶点用∞表示[^3]。 **2. 迭代更新矩阵(k=1,2,3)** 对于每个中间点$k$,更新所有$i,j$对的最短路径: $$ D^{(k)}[i][j] = \min(D^{(k-1)}[i][j], D^{(k-1)}[i][k] + D^{(k-1)}[k][j]) $$ **步骤k=1(中间点为A)** 更新规则:检查所有路径是否经过A更优 - B→C原为2,经过A的新路径B→A→C=6+3=9,不更新 - C→B原为∞,经过A的新路径C→A→B=3+4=7 更新后矩阵: $$ D^{(1)} = \begin{bmatrix} 0 & 4 & ∞ \\ 6 & 0 & 2 \\ 3 & 7 & 0 \\ \end{bmatrix} $$ **步骤k=2(中间点为B)** - A→C原为∞,经过B的新路径A→B→C=4+2=6 - C→A原为3,经过B的新路径C→B→A=7+6=13(不更新) 更新后矩阵: $$ D^{(2)} = \begin{bmatrix} 0 & 4 & 6 \\ 6 & 0 & 2 \\ 3 & 7 & 0 \\ \end{bmatrix} $$ **步骤k=3(中间点为C)** - A→B原为4,经过C的新路径A→C→B=6+7=13(不更新) - B→A原为6,经过C的新路径B→C→A=2+3=5 更新后矩阵: $$ D^{(3)} = \begin{bmatrix} 0 & 4 & 6 \\ 5 & 0 & 2 \\ 3 & 7 & 0 \\ \end{bmatrix} $$ #### 伪代码实现 ```python def floyd(W): n = len(W) D = W.copy() for k in range(n): for i in range(n): for j in range(n): D[i][j] = min(D[i][j], D[i][k] + D[k][j]) return D ``` #### 算法特性 1. **时间复杂度**:$O(n^3)$,三重循环结构 2. **空间复杂度**:$O(n^2)$,存储距离矩阵 3. **动态规划特征**:每个$D^{(k)}$表示仅经过前k个中间点的最短路径[^4] [^2]: 引用自多源最短路径算法对比说明 : 算法复杂度数据来源 [^4]: 动态规划原理图解证明
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值