逻辑算法大赛辅导--Floyd-Warshall算法[整理]

zengyiCSTC 2004-02-17 08:31:43
/*****这份材料详细讲述了Floyd-Warshall算法,及其实现,来自复旦大学BBS,希望对准备参加大赛的同学能够有用*****/
下面的讨论中考虑一个有向图G(V,E),顶点记作V1,V2,..Vn,G的每条边赋有一个权值,w(i,j)表示边(i,j)上的权,如果(i,j)不相邻则我们认为w(i,j) = 无穷大。


对于任意一对顶点Vi,Vj,考察从Vi到Vj且中间顶点属于集合{V1,V2,...Vk}的所有
路径,设其中最短的一条路径为P。若Vk不是路径P的中间节点,则P的所有中间节
点都属于集合{V1,V2,..Vk-1},因此从Vi到Vj且中间顶点属于集合{V1,V2,...
Vk-1}的最短路径也是从Vi到Vj且中间顶点属于集合{V1,V2,...Vk}的最短路径;若
Vk是P的中间节点,我们把P分解成P1={Vi,...Vk}和P2={Vk,..,Vj},显然P1是从
Vi到Vk的一条最短路径且满足所有的中间顶点均属于集合{V1,V2,..Vk-1},类似的
,P2从Vk到Vj的一条最短路径且满足
所有的中间顶点均属于集合{V1,V2,..Vk-1}。这个性质是关键!!这就是动态规划
的最优子结构性质。

设d(k,i,j)是从顶点Vi到Vj且中间顶点属于集合{V1,V2,...Vk}的最短路径长度,
显然当k=0的时候,从顶点i到顶点j不存在中间节点,因此它至多包含一条边,即

d(0,i,j)=w(i,j);
设p(k,i,j)为从顶点顶点Vi到Vj且满足所有的顶点均属于集合{V1,V2,...Vk}的最
短路径长度上j 的前一个节点,显然k=0时,若(i,j)存在边则p(0,i,j)=i,否则
p(0,i,j)= 无穷大;

根据前面分析的最优子结构性质,我们可以得到递归公式:

当k>0的时候,d(k,i,j)=min {d(k-1,i,j), d(k-1,i,k)+d(k-1,k,j)},

理解上面这个公式是理解这个算法的关键所在!!

另外我们还有公式:

当k>0且 d(k-1,i,j) <= d(k-1,i,k)+d(k-1,k,j) 时, p(k,i,j) = p(k-1,i,j)
当k>0且 d(k-1,i,j) > d(k-1,i,k)+d(k-1,k,j) 时, p(k,i,j) = p(k-1,k,j)

如果一共有n个顶点,则从Vi 到 Vj 的最短路径的中间节点属于集合{V1,V2,...
Vn},所以应该是 d(n,i,j);

根据上面的这些公式我们可以递推地求出每两对顶点之间的最短路径,下面举一个
具体的例子说明计算的过程:

考虑一个有向图,顶点数n=5,下面的每一行代表一条有向边(i,j)以及边上的权
w(i,j)

1->2 w(1,2) = 3
1->3 w(1,3) = 8
1->5 w(1,5) = -4
2->3 w(2,3)=4
2->4 w(2,4)=1
2->5 w(2,5)=7
4->1 w(4,1)=2
4->3 w(4,3)= -5
5->4 w(5,4)=6

下面给出对于k=0,1,2,...n的所有的d矩阵:

┌ ┐ ┌ ┐
│ 0 3 8 ∞ -4│ │∞ 1 1 ∞ 1│
│∞ 0 ∞ 1 7│ │∞ ∞ ∞ 2 2│
k=0, d = │∞ 4 0 ∞ ∞│ p =│∞ 3 ∞ ∞ ∞│
│ 2 ∞ -5 0 ∞│ │ 4 ∞ 4 ∞ ∞│
│∞ ∞ ∞ 6 0│ │∞ ∞ ∞ 5 ∞│
└ ┘ └ ┘

这一步求出的d(i,j)是从 i 直接到 j 的最短距离;
因为w(1,2)=3,所以d(1,2)=3, 因为w(1,3)=8,所以d(1,3)=8, 因为V1到V4无边,
所以d(1,4)=∞, ...
因为从V1到V2的前驱是1,所以p(1,2)=1, 从 V2到V4的前驱是2,所以p(2,4)=2,因
为从V2到V1无边,所以p(2,1)=∞,...


┌ ┐ ┌ ┐
│ 0 3 8 ∞ -4│ │∞ 1 1 ∞ 1│
│∞ 0 ∞ 1 7│ │∞ ∞ ∞ 2 2│
k=1, d = │∞ 4 0 ∞ ∞│ p =│∞ 3 ∞ ∞ ∞│
│ 2 5 -5 0 -2│ │ 4 1 4 ∞ 1│
│∞ ∞ ∞ 6 0│ │∞ ∞ ∞ 5 ∞│
└ ┘ └ ┘

这一步求出的d(i,j)是从Vi到Vj且中间经过了节点集合 { V1 } 的最短距离;
注意到d(4,2)=5,而前一个矩阵中d(4,2)=∞,这是因为V4和V2不直接相邻,但是
V4-V1-V2,且这时从V4到V2中间经过了节点集合 { V1 } 的最短距离;
在p矩阵中,p(4,2) =1,说明从V4到V2中间经过了节点集合 { V1 } 的最短路径上
V2的前驱是V1


┌ ┐ ┌ ┐
│ 0 3 8 4 -4│ │∞ 1 1 2 1│
│∞ 0 ∞ 1 7│ │∞ ∞ ∞ 2 2│
k=2, d = │∞ 4 0 5 11│ p =│∞ 3 ∞ 2 2│
│ 2 5 -5 0 -2│ │ 4 1 4 ∞ 1│
│∞ ∞ ∞ 6 0│ │∞ ∞ ∞ 5 ∞│
└ ┘ └ ┘

这一步求出的d(i,j)是从Vi到Vj且中间经过了节点集合 { V1,V2 } 的最短距离





┌ ┐ ┌ ┐
│ 0 3 8 4 -4│ │∞ 1 1 2 1│
│∞ 0 ∞ 1 7│ │∞ ∞ ∞ 2 2│
k=3, d = │∞ 4 0 5 11│ p =│∞ 3 ∞ 2 2│
│ 2 -1 -5 0 -2│ │ 4 3 4 ∞ 1│
│∞ ∞ ∞ 6 0│ │∞ ∞ ∞ 5 ∞│
└ ┘ └ ┘

这一步求出的d(i,j)是从Vi到Vj且中间经过了节点集合 { V1,V2,V3 } 的最短距
离;




┌ ┐ ┌ ┐
│ 0 3 -1 4 -4│ │ ∞ 1 4 2 1│
│ 3 0 -4 1 -1│ │ 4 ∞ 4 2 1│
k=4, d = │ 7 4 0 5 3│ p =│ 4 3 ∞ 2 1│
│ 2 -1 -5 0 -2│ │ 4 3 4 ∞ 1│
│ 8 5 1 6 0│ │ 4 3 4 5 ∞│
└ ┘ └ ┘

这一步求出的d(i,j)是从Vi到Vj且中间经过了节点集合 { V1,V2,V3,V4 } 的最
短距离;



┌ ┐ ┌ ┐
│ 0 1 -3 2 -4│ │∞ 3 4 5 1│
│ 3 0 -4 1 -1│ │ 4 ∞ 4 2 1│
k=5, d = │ 7 4 0 5 3│ p =│ 4 3 ∞ 2 1│
│ 2 -1 -5 0 -2│ │ 4 3 4 ∞ 1│
│ 8 5 1 6 0│ │ 4 3 4 5 ∞│
└ ┘ └ ┘

这一步求出的d(i,j)是从Vi到Vj且中间经过了节点集合 { V1,V2,V3,V4,V5 } 的
最短距离;即求出了图中两两顶点之间的最短距离。

实际编程中,只需要两个数组就可以存储d,p矩阵了,计算出d,p矩阵以后可以根据
p矩阵递归的求出从Vi到Vj的最短路径上的所有顶点。

这个算法就是floyd-warshall算法,复杂度为O(n^3)。这个算法对于求一般的有向
图中的两两顶点之间最短路径是很有效的,但是如果是稀疏图(图的边数远小于顶
点数),则还
有一种Jobnson算法更加有效,复杂度可以达到O( V^2*lgV+VE) [ 使用
Fibonacci堆作为数据结构 ] 或者O(VElgV) [使用二叉堆作为数据结构]




const int INFINITY = 500000; // 无穷大
const int MAX_NODE = 100; // 顶点数目
typedef int Graph[MAX_NODE][MAX_NODE];
typedef int Path[MAX_NODE];

/*
Floyd Washall 算法
求每对顶点之间最短距离,复杂度O(V^3)
G是图,如果i,j不相邻则G[i][j] = INFINITY
n是顶点数目,D保存最短距离,P保存最短路径的前驱
*/
void Floyd(Graph G, int n, Graph D, Graph P)
{
int i, j, k;

for (i = 0; i < n; i++) {
for (j = 0; j < n; j++) {
D[i][j] = G[i][j];
P[i][j] = i;
}
D[i][i] = 0;
}

for (k = 0; k < n; k++) {
for (j = 0; j < n; j++) {
if (D[i][j] - D[i][k] > D[k][j]) { // 注意,这里为防止溢出写
成减法而不是加法
D[i][j] = D[i][k] + D[k][j];
P[i][j] = P[k][j];
}
}
}
}
}

//来源:月光华 bbs.fudan.edu.cn[FROM: 10.11.4.245]
...全文
132 2 打赏 收藏 转发到动态 举报
写回复
用AI写文章
2 条回复
切换为时间正序
请发表友善的回复…
发表回复

249

社区成员

发帖
与我相关
我的任务
社区描述
其他产品/厂家
社区管理员
  • 其他
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧