最大流算法复杂度分析

Ford-Fulkerson算法(1956)

非常简单,直接在残量网络上dfs找到一条增广路,然后更新残差图,不断重复,直到找不到增广路。然后得到的就是最大流。

如果所有的边的容量都是0到U的整数,那显然最大流最大是nU。每次增广都至少使得总流量增加1,所以最多有nU次增广。找增广路的复杂度是\(O(m)\),m是边数,所以总的复杂度就是\(O(mnU)\)。如果U是1,那总复杂度就是\(O(mn)\)

注意,如果容量可能是无理数的话,那这个算法可能永远不会停止。例子我也不会举。

Capacity Scaling

为了使得找到的增广路容量尽可能大,先在只含容量大于等于\(\Delta\)的边的子图中不停找增广路,直到找不到为止。然后将\(\Delta\)减半,再重复,直到\(\Delta\)变成0。可见这个优化只在所有边的容量都是整数的情况才有用。

设U为最大的边容量,\(\Delta\)的初值设为\(2^{\lfloor \log_2 U \rfloor}\)。显然,每个scaling phase一开始的时候子图的边容量都在\([\Delta, 2\Delta)\)的范围里,所以这个scaling phase最多能够增广出\(O(m\Delta)\)的流量。由于每次增广最少能增广出\(\Delta\)的流量,所以在一次scaling phase中,增广次数为\(O(m)\)。每次找增广路的复杂度是\(O(m)\),最多有\(O(log(U))\)个scaling phase,所以总复杂度是\(O(m^2\log(U))\)

Dinic:最短增广路

Dinic用BFS找增广路,这样每次找到的都是最短增广路。

L1. 找到的最短增广路的长度单调不减。

将最短增广路长度不变的增广称为正常增广,将长度严格单调增加的增广称为特殊增广。

L2. 最多连续m次正常增广之后必有一次特殊增广。

L3. 一组连续的正常增广消耗的总时间为O(mn)。

为了证明这些性质,先引入admissible graph的概念。设d(v)为v到t的距离,l(e)是边e的长度(通常为1),找到残差图中只含满足d(v) = d(w) + l(v, w)的边(v, w)的子图,也就是说每条边都是某个点到t的最短路上的边。在这些边里进一步筛选出在s到t的最短路上的边,这些边构成的子图就是admissible graph,记为L。

L1证明

老师的证明:显然,s到t的路径中,经过admissible graph中的边的反向边的路径都比最短路径长。所以最短增广路一定是在admissible graph中的。所以最短路径的长度单调不减。

我觉得这个证明有点模糊。这里我用反证法来证明一下。

假如最短增广路的长度不是单调不减的,那么必然存在先后两条增广路a和b,使得a比b长。显然,在增广a的时候,增广路b是不存在的,否则就先找到b了。为什么增广完a之后,b就存在了呢?显然是因为b里有一些边原先是只有反向边的,然后a使用了这些反向边,然后就得到正向边了。我们假设这些反向边里,最靠近s的边是(A, B),那么a就是由一条从s到B的路径\(a_1\),一条从A到t的路径\(a_2\),以及边(B, A)构成的。b由s到A的路径\(b_1\),B到t的路径\(b_2\),以及边(A, B)构成。我们前面说过,(B, A)是最靠近t的反向边,这就意味着\(b_1\)在增广a的时候也存在。假设(B, A)的长度为\(l_1\),那么有如下表达式

\[\begin{aligned} \begin{cases} a_1 + l_1 + a_2 \le b_1 + a_2 \\ b_1 + l_1 + b_2 < a_1 + l_1 + a_2 \end{cases} \end{aligned} \]

\(b_2 < a_2 + l_1\)。所以,\(b_2\)这条路径在增广a时是不存在的,这说明\(b_2\)里至少有一条由增广a得到的反向边。假设这些反向边中离t最近的是(C, D),\(b_2\)由从B到C的路径\(b_{21}\),从D到t的路径\(b_{22}\),以及边(C, D)构成。显然路径\(b_{22}\)在增广a的时候也存在。

假设边(D, C)在\(a_1\)中,那么\(a_1\)由s到D的路径\(a_{11}\),C到B的路径\(a_{12}\),以及边(D, C)构成。在增广a的时候,由于路径\(b_{22}\)也存在,所以其实还可以走\(a_{11}\)从s到D,再走\(b_{22}\)从D到t。由于\(a_{11}+b_{22} < a_1 + b_2 < a_1 + a_2 + l_1\),所以这条路比a还短,矛盾。

所以(D, C)在\(a_2\)中,那么其实又得到了一个子情况:找从B到t的增广路,先找到了更长的路径\(a_2+l_1\),再找到了更短的路径\(b_2\)。不断套用上面的逻辑,会发现总是有反向边。但是\(b\)里的反向边是有限的,所以假设不成立,所以最短增广路的长度总是单调不减的。

L2证明

在s到t的最短路长度不变的情况下,显然每次增广产生的反向边都不会被加入到L中(L是admissible graph),因为经过这些反向边的路径的长度都比最短路长。而且每次增广都至少消耗掉L中的一条边。由于L中最多有m条边,所以最多做m次最短增广路之后,L就被消耗完了,最短增广路长度就开始增加了。

L3证明

求出admissible graph L。在L上从s开始做dfs。显然dfs到的任何一条到达t的路径都是正常增广路径,增广之后就可以至少消耗掉一条边,所以最多进行O(m)次增广(其实是L2的结论)。因为L是一个DAG,所以每次dfs都是\(O(n)\)的。所以总的复杂度是\(O(mn)\)

我们注意到,增广的时候要对路径上的每条边减去流量。这种操作其实很适合用LCT做。所以我们考虑用LCT优化之。

定义LCT的各操作如下:

  • make-tree(v): 让点v单独成树。

  • link(v, w): 把v挂到w上

  • cut(v, w): 让v不再是w的子节点

  • find-root(v): 返回v所在的树的根(用来检查两个节点是否在同一棵树里)。这个好像其实没用?因为加边的时候肯定没有环。

  • min-cost(v): 返回从v到root(v)的路径上的值最小的边。老师PPT上说是返回所有值最小的边的集合,我觉得这样的话复杂度可能就不是\(O(\log n)\)了。

  • update(v, x): 把x加到v到root(v)的路径上的所有边的值上。应该是用了懒惰标记。

然后维护一棵以t为根的树(让每个除了t的点都有一条出边),然后调用min-cost(s)就可以找到s到t的路径上容量最小的边(v, w),然后调用update从这些边上减掉容量,然后调用cut把这条容量最小的边删掉。这时v成了s所在的树的根了,如果v还有出边,就直接把这条边加到LCT里,因为这条边一定是指向以t为根的树的某个节点的,因为L是一个DAG,如果v的出边指向以v为根的树的节点的话,就有环了。如果v没有出边了,就把v删掉(应该是通过把指向v的那些边都cut掉),然后会得到一个森林。同样检查这些森林的根节点还有没有出边,有的话就连上,肯定能连到另一棵树上,没有的话再删。不断重复,直到所有没有被删除的节点再次都有一条出边。然后继续调用min-cost(s)找增广路。注意由于我们这里min-cost只返回一条边,所以可能会有一些容量已经减为0的边没有被cut掉,所以min-cost可能会返回容量为0的边。这时就直接把这条边cut掉,然后继续就好了。

每轮的时间复杂度是\(O(\log n)\)的,然后每轮都至少会删掉一条边,所以最多m轮。总复杂度是\(O(m\log n)\)

总复杂度

每组正常增广的时间复杂度是\(O(m\log n)\)。由于最短路长度最多增加n次,所以最多n组正常增广,总时间是\(O(mn\log n)\)

单位容量图的分析

即边的容量都是1的图(有向或者无向)。

最多做\(O(n^{2/3})\)组增广

下面先证明单位容量有向图上的一个引理:如果最大流是M,那么s到t的最短路的长度最大为\(\frac{2n}{\sqrt M}\)

证明:我们先给这个图根据与s的距离来分层,\(|V_i|\)表示s到这点的距离为\(i\)的点构成的点集。显然,\(V_{i-1}\)\(V_i\)的边的总容量大于M(最大流最小割定理),而总容量又等于边数,边数最大为\(|V_{i-1}| |V_i|\),所以有\(|V_{i-1}| |V_i| \ge M\),所以\(|V_{i-1}| \ge \sqrt M\)或者\(|V_i| \ge \sqrt M\)。假设s到t的最短路的长度为\(l\),那么\(\sum_{i=0}^l |V_i| = n\),而\(\sum_{i=0}^l |V_i| \ge \lfloor \frac{l+1}{2} \rfloor \sqrt M\),所以\(\lfloor \frac{l+1}{2} \rfloor \sqrt M \le n\)\(l \le \frac{2n}{\sqrt M}\)

对于无向图,边的重度最多为2,所以最短路长度仍然为\(O(\frac{n}{\sqrt M})\)。简单起见,下面仅讨论有向图,无向图同理。

如果当前流量\(\le M - n^{2/3}\),那么其残差图的最大流就\(\ge n^{2/3}\),由于残差图仍然是个单位容量图,由前面的引理,所以残差图的s到t的最短路长度\(\le \frac{2n}{\sqrt{n^{2/3}}} = 2n^{2/3}\),即当前流量达到\(M - n^{2/3}\)最多要做\(O(n^{2/3})\)组增广。当前流量大于\(M - n^{2/3}\)之后,其残差图的最大流就小于\(n^{2/3}\)了,由于每组增广最少能给流量加一,所以最多再做\(n^{2/3}\)组增广即可达到最大流。所以总的来说,最多做\(O(n^{2/3})\)组增广。

最多做\(O(\sqrt m)\)组增广

跟上面的差不多。先证明引理:如果最大流是M,那么s到t的最短路的长度最大为\(\frac{m}{M}\)

证明:同样先按照与s的距离分成\(l+1\)层,两层之间的边的容量至少为\(M\),那么总边数至少为\(lM\),所以\(lM\le m\)\(l\le \frac{m}{M}\)

如果当前流量\(\le M - \sqrt m\),那么其残差图的最大流\(\ge \sqrt m\),由引理,残差图的s到t的最短路长度\(\le \frac{m}{\sqrt m} = \sqrt m\),所以当前流量达到\(\le M - \sqrt m\)最多要做\(O(\sqrt m)\)组增广。当前流量达到\(\le M - \sqrt m\)后,由于每组增广最少能给流量加一,所以最多再做\(\sqrt m\)组增广即可达到最大流。所以总的来说,最多做\(O(\sqrt m)\)组增广。

单位容量图总复杂度

每次在admission graph里找到所有正常增广路时,即做一组增广时,每条边都只会被访问一遍,所以时间复杂度是\(O(m)\)。所以总复杂度是\(O(\min(\sqrt m, n^{2/3}) \cdot m)\)

参考文献

Even S, Tarjan R E. Network flow and testing graph connectivity[J]. SIAM journal on computing, 1975, 4(4): 507-518.

Dinic算法的复杂度分析及证明

老师PPT里A simple algorithm for undirected graph之后的都没看懂

posted @ 2024-09-28 13:41  寻找繁星  阅读(12)  评论(0编辑  收藏  举报