上一周去了一趟说走就走的治疗之旅,所以算法课都没能上。:( 不过,人生有过这样一次,很足够。只要永远做自己认为对的事情就不会有跨不过去的坎。
跟上周一样,这一周的内容包含几个小部分,分别为最短路径动态规划、所有点对之间的最短路径和网络流。
第一部分:最短路径动态规划
对于一个有向图G=(V, E), 每一条边权重为cvw(权重可为负), 问题是找到从节点s到t的最短的路径。如果边的权重中有负值,则Dijkstra方法不适用。因此我们想到一个办法,给每一个权值加上一个正常数使得每一条边的权重都为非负,这个方法也不行。
这里定义一个负(成本)环。如下图所示,顾名思义,所有边的权值的和为负数。
如果一个从s到t的路径中包含一个负(成本)环,那s-t之间不存在最短路径,否则,存在一个最短路径。如下图。
下面用动态规划来解决最短路径的问题。
定义OPT(i, v) 表示使用最多i条边的最短的v到t的路径P。
1) P中使用最多i-1条边
OPT(i, v) = OPT(i - 1, v)
2) P使用第i条边
如果(v, w)是第一条边,那么OPT使用(v, w), 然后选择最佳的w-t路径使用最多i-1条边
通过之前的观察,可以得出,如果没有负环,那么OPT(n - 1, v)为最短的v-t路径的长度。
实现代码如下:
时间复杂度为O(mn), 空间复杂度为O(n^2).
下面提出一种改进。
保持一个矩阵M[v]为我们目前能够找到的最短的v-t路径。除非M[v]在前一个迭代中改变了,否则不需要检查(v, w)形成的边。这里有这样一个定理,在整个算法中,M[v]是v-t路径的长度,在i轮更新之后,M[v]不会大于使用不多于i条边的最短路径的长度。内存消耗为O(m+n),时间复杂度为O(mn)最坏情况,但是实际会更快。
Bellman-Ford实现算法如下:
使用以上算法可以证明,如果对于所有的v有OPT(n, v) = OPT(n - 1, v),那么就没有负环。
如果OPT(n, v) < OPT(n - 1, v), 那么任何从v到t的最短路径都包含一个环W,并且,W包含负数的权值边。
以上两条可以用于检测负环。Bellman-Ford算法的时间复杂度为O(mn), 空间复杂度为O(m+n).<