Floyd~写起来最简单的算法~没有之一!!!然而,理解起来却一点也不简单~
先来看看算法实现:
for (int k = 0; k < n; k++)
for (int i = 0; i < n; i++)
for (int j = 0; j < n; j++)
if (d[i][j] > d[i][k] + d[k][j])
d[i][j] = d[i][k] + d[k][j];
其中n是顶点数量,d[i][j]表示点i到点j的最短距离,那么问题来了: k是什么鬼?!
要回答这个问题,我们要先看看算法的原理是什么。。。
随便找个Floyd的解释,都会告诉我们Floyd的核心思想是动态规划,
其状态转移方程是: d[i][j] = min(d[i][j], d[i][k] + d[k][j])
受图论影响,大家都明白k也就是图中一个点,但这方程不是松驰操作吗?!
说好的动态规划呢?
代码外层一个for (int k = 0; k < n; k++)搞定了?
特么一脸懵逼好不好!!~
为了解释这个问题,引进一个概念
Ak(i,j) : 表示i到j的路径中,没有索引超过k的点的最短路径
比如, i=1,j=2, k=5,路径可以1->3->2, 1->5->3->2, 但不能是1->6->2
用脚趾头看出,当k=n-1时,Ak(i,j) = d[i][j], 既最短路径
相信大家也都会求 A0(i,j)
所以现在的问题是:怎么由Ak-1(i,j) 推出Ak(i,j) ? 重点来了!!敲黑板!!
这里其实要分情况讨论:
1. 如果 Ak(i,j) 没有经过点k, 那么 Ak(i,j) = Ak-1(i,j)
2. 如果 Ak(i,j) 经过点k, 那么 Ak(i,j) = Ak-1(i,k) + Ak-1(k,j)
所以有:
Ak(i,j) = min(Ak-1(i,j), Ak-1(i,k) + Ak-1(k,j))
于是,我们既然能求 A0(i,j),也就能求出A1(i,j),最后An-1(i,j)
这也就是为什么Floyd最外层是 for (int k = 0; k < n; k++)
d[i][j] = min(d[i][j], d[i][k] + d[k][j]) 既是松驰操作,也是动态规划的状态转移方程