Floyd-Warshall算法
(Floyd-Warshall algorithm)是解决任意两点间的
最短路径
的一种
算法
,可以正确处理
有向图
或负权的最短路径问题,同时也被用于计算有向图的传递闭包。
从图的带权
邻接矩阵A=[a(i,j)] n×n开始,
递归地进行n次更新,即由矩阵D(0)=A,按一个公式,构造出矩阵D(1);又用同样地公式由D(1)构造出D(2);……;最后又用同样的公式由D(n-1)构造出
矩阵D(n)。
矩阵D(n)的i行j列元素便是i号顶点到j号顶点的
最短路径长度,称D(n)为图的
距离矩阵,同时还可引入一个后继节点矩阵path来记录两点间的最短路径。
采用的是(松弛技术),对在i和j之间的所有其他点进行一次松弛。所以
时间复杂度为O(n^3);
设为从到的只以集合中的节点为中间节点的最短路径的长度。
- 若最短路径经过点k,则;
- 若最短路径不经过点k,则。
因此,。
其
状态转移方程如下: D[i,j]:=min{
D[i,j],D[i,k]+D[k,j]}
map[i,j]表示i到j的最短距离,K是穷举
i,j的断点,D[n,n]初值应该为0,或者按照题目意思来做。
当然,如果这条路没有通的话,还必须特殊处理,比如没有D[i,k]这条路。
Floyd-Warshall算法的时间复杂度为,空间复杂度为。
Floyd-Warshall算法的描述如下:
for k ← 1 to n do for i ← 1 to n do for j ← 1 to n do if () then ← ;
其中表示由点到点的代价,当为 ∞ 表示两点之间没有任何连接。
在实际算法中,为了节约空间,可以直接在原来空间上进行迭代,这样空间可降至二维。
大概的代码为:
for(int k=1;k<=n;k++)
{
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++)
{
if(ans[k-1][i][k]==无穷大||ans[k-1][k][j]==无穷大)
{
ans[k][i][j]=ans[k-1][i][j]; //保持原来的值,即从i到j允许经过前k个点和允许经过前k-1个节点时最短路径长度相同
continue;
}
if(ans[k-1][i][j]==无穷大||ans[k-1][i][k]+ans[k-1][k][j]<ans[k-1][i][j])
{
ans[k][i][j]=ans[k-1][i][k]+ans[k-1][k][j];
}
else
{
ans[k][i][j]=ans[k-1][i][j];
}
}
}
}
经过这么多次迭代后,最后a到b的最短路径结果为ans[n][a][b].
与此同时,我们注意到,在通过ans[k-1][i][j]的值来递推求ans[k][i][j]的值时,所有的ans[k][i][j]值将由ans[k-1][i][j]与ans[k-1][i][k]+ans[k-1][k][j]的大小关系来确定,但同时ans[k][i][k]和ans[k][k][j]必定与ans[k-1][i][k]和ans[k-1][k][j]的值是相同的,即这些值不会因为本次更新而发生变化。所以我们将代码简化为:
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]==无穷大||ans[k][j]==无穷大) continue;
if(ans[i][j]==无穷大||ans[i][k]+ans[k][j]<ans[i][j])
{
ans[i][j]=ans[i][k]+ans[k][j];
}
}
}
}
这样原本空间复杂度O(N3)变为O(N2)了。每次跟新直接在该二位数组上就OK。