2018.3.10 bellman-ford algorithm, floyd-warshall algorithm and johnson's algorithm

Posted dynasty919

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了2018.3.10 bellman-ford algorithm, floyd-warshall algorithm and johnson's algorithm相关的知识,希望对你有一定的参考价值。

这周还是继续dynamic programming,不过回到了图算法,因为之前讲图算法的时候还没有讲到动态规划,而这三个算法要么本身是动态规划算法,要么要用到动态规划算法。

1.bellman-ford是一种求无负权值的环路的有向图两点间最小路径的算法,也就是说和dijkstra解决的是同一个问题。但是dijkstra无法计算有负权值的边时的情况,bellman-ford可以处理这种情况。如果给定的图有负权值的环路的话,那么就会出现负无穷大的最小路径,那当然是处理不了的,不过如果只有正权值的环路,那么bellman-ford是可以处理的。bellman-ford的核心trick是:从给定源点,到任何一点的最小路径,它的边数不应该超过n-1条。因为超过了的话就意味着至少经过了某个点两遍,那就意味着有负权值的环路,那么就不对了。

那么在知道了这个trick之后,我们就只需要做n-1次循环就可以了:初始化除源点以外的每个结点到源点的最小路径为正无穷大,然后每次循环,我们从源点向外延伸一条边,更新一次能延伸到的所有结点的最小路径。做n-1次循环后,得到的所有点的最小路径,就是我们要得到的结果。不过如果有负权值的环路的话,这个算法是不对的,但是也没关系,我们只需要再做一次这个循环,再延伸一条边,如果各点相对源点的最小路径有变化(也就是说有减小),那么根据某个我没看的证明(-_-!),这时就出现了负权值环路了,那么虽然算法不对,至少我们也检测到了负权值环路,知道了问题不可解。如果再做一次循环之后各点相对源点的最小路径没有变化,那就意味着没有负权值环路,那么得到的就是正确结果。

bellman-ford是O(mn)的,比dijkstra的O(mlg(n))慢不少,不过适用范围是更广的,而且据说还能分布式计算。

2.如果要计算一个给定有向图中,任意两点之间的最小路径,就是说每两点的组合都要求最小路径,那么很简单,跑n次bellman-ford就行了。不过这样子非常慢。floyd-warshall会更快一些的解决这个问题。floyd-warshall是一个很标准的dynamic programming算法。它把所有结点标记出编号1~n,那么从点i到点j的最小路径怎么求呢,我们像一般的动态规划的思路一样,把它变成subproblem,方法是从点i到点j的路径,只允许经过编号1~k-1的点,那么相对1~k,就是一个subproblem吧。

实际上这很像最早讲动态规划时举的max weight independent set的栗子,从up到bottom的过程中,每个subproblem相比上一个subproblem,只是多了一个编号为k的点是不是在路径中的选择。也就是说,这里是非常经典的两种方案的动态规划模型。对于从i到j,只允许经过1~k-1的最短路径的问题:如果k不在路径中,那么问题等同;如果k在路径中,那么把问题拆分为 “从i到k 加上 从k到j” 就解决了。

最后的算法是k,i,j从1到n的三层循环。复杂度为O(n^3),比跑n次bellman-ford的O(mn^2)要好,因为对于密集图,m最大是可以有n^2的。所以越密集的图,floyd-warshall的优势越大。

3.johnson‘s algorithm是解决什么问题呢,和floyd-warshall一样,也是解决给定图所有点组合间最小路径的问题的。我们之前说解决这个问题可以跑n次bellman-ford,虽然比较慢。johnson屌在哪呢,他实际上是跑了n次dijkstra来解决这个问题,而且这算法居然还能用在有负权值的边的情况!

trick在于,这算法是先用一种酷炫的方法把有负边的原图转化为没有负边的新图,然后对新图跑n边dijkstra,然后再把得到的新图的最短路径转化为原图的。

这里这种把有负边的原图转化为没有负边的新图的办法,叫做reweight technique。这个办法是:在原图基础上,加一个新结点,把新结点与原图的每个结点都用一条权值为0的边连通,然后以新结点为源点,对原图的各结点求最小路径——很显然,就是跑一遍bellman-ford。得到的每个最小路径,就是原图该结点的weight。这个weight能干啥呢:比如说原图有一条边,是从i到j,权值为负,那我们把权值加上i的weight,减去j的weight,得到的新权值,根据某个我没看的证明(-_-!!),这个新权值一定是正的。对原图所有的变都做这样的操作,那么就得到了一个所有边权值都为正的新图。

这个方法真的很吊,因为按这样去转化,任意两点之间的路径,其实只和原路径,以及这两个点的weight有关,和其他点的weight都没有关系,这样子后面往回转化的时候也非常好转化。

然后就是对新图n遍dijkstra了。得到的最短路径要变回去,这次减去i的weight,加上j的weight,就是原图的最短路径了。这个算法相当于1遍bellman-ford加n遍dijkstra,最后是O(mn lg(n))的,可以说是很快了。

以上是关于2018.3.10 bellman-ford algorithm, floyd-warshall algorithm and johnson's algorithm的主要内容,如果未能解决你的问题,请参考以下文章

Bellman-Ford算法

Bellman-ford 单源最短路径算法

Bellman-Ford 算法

Bellman-Ford算法

Bellman-Ford与SPFA

Bellman-Ford算法的改进---SPFA算法