图中两点所有路径_【洛谷日报#242】Johnson 全源最短路径算法学习笔记

Johnson 和 Floyd 一样,是一种能求出无负环图上任意两点间最短路径的算法。该算法在 1977 年由 Donald B. Johnson 提出。

Part 1 算法概述

任意两点间的最短路可以通过枚举起点,跑

次 Bellman-Ford 算法解决,时间复杂度是
的,也可以直接用 Floyd 算法解决,时间复杂度为

注意到堆优化的 Dijkstra 算法求单源最短路径的时间复杂度比 Bellman-Ford 更优,如果枚举起点,跑

次 Dijkstra 算法,就可以在
(本文中的 Dijkstra 采用
priority_queue 实现,下同)的时间复杂度内解决本问题,比上述跑
次 Bellman-Ford 算法的时间复杂度更优秀,在稀疏图上也比 Floyd 算法的时间复杂度更加优秀。

但 Dijkstra 算法不能正确求解带负权边的最短路,因此我们需要对原图上的边进行预处理,确保所有边的边权均非负。

一种容易想到的方法是给所有边的边权同时加上一个正数

,从而让所有边的边权均非负。如果新图上起点到终点的最短路经过了
条边,则将最短路减去
即可得到实际最短路。

但这样的方法是错误的。考虑下图:

c989c3f298d0af01524fae2767d0a0b6.png

的最短路为
,长度为

但假如我们把每条边的边权加上

呢?

6eb84bfa2dab72b2cb4e4c31fd5c21d5.png

新图上

的最短路为
,已经不是实际的最短路了。

Johnson 算法则通过另外一种方法来给每条边重新标注边权。

我们新建一个虚拟节点(在这里我们就设它的编号为

)。从这个点向其他所有点连一条边权为
的边。

接下来用 Bellman-Ford 算法求出从

号点到其他所有点的最短路,记为

假如存在一条从

点到
点,边权为
的边,则我们将该边的边权重新设置为

接下来以每个点为起点,跑

轮 Dijkstra 算法即可求出任意两点间的最短路了。

容易看出,该算法的时间复杂度是

Q:那这么说,Dijkstra 也可以求出负权图(无负环)的单源最短路径了?
A:没错。但是预处理要跑一遍 Bellman-Ford,还不如直接用 Bellman-Ford 呢。

Part 2 正确性证明

为什么这样重新标注边权的方式是正确的呢?

在讨论这个问题之前,我们先讨论一个物理概念——势能。

诸如重力势能,电势能这样的势能都有一个特点,势能的变化量只和起点和终点的相对位置有关,而与起点到终点所走的路径无关。

势能还有一个特点,势能的绝对值往往取决于设置的零势能点,但无论将零势能点设置在哪里,两点间势能的差值是一定的。

接下来回到正题。

在重新标记后的图上,从

点到
点的一条路径
的长度表达式如下:

化简后得到:

无论我们从

走的是哪一条路径,
的值是不变的,这正与势能的性质相吻合!

为了方便,下面我们就把

称为
点的势能。

上面的新图中

的最短路的长度表达式由两部分组成,前面的边权和为原图中
的最短路,后面则是两点间的势能差。因为两点间势能的差为定值,因此原图上
的最短路与新图上
的最短路相对应。

到这里我们的正确性证明已经解决了一半——我们证明了重新标注边权后图上的最短路径仍然是原来的最短路径。接下来我们需要证明新图中所有边的边权非负,因为在非负权图上,Dijkstra 算法能够保证得出正确的结果。

根据三角形不等式,新图上任意一边

上两点满足:
。这条边重新标记后的边权为
。这样我们证明了新图上的边权均非负。

至此,我们就证明了 Johnson 算法的正确性。

Part 3 参考代码

Reference

  • Johnson's algorithm - Wikipedia
  • 《算法导论(中译本,第 3 版)》,25.3 用于稀疏图的 Johnson 算法,409-411 页
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值