[Algorithm] Maximum Flow
Ref MIT: lecture-13-incremental-improvement-max-flow-min-cut/
Ford Fulkerson algorithm for finding maximal flow in a flow network:
- Keep adding flow through new augmenting paths for as long as it is possible;
- When there are no more augmenting paths, you have achieved the largest possible flow in the network.
Ford Fulkerson最大流算法 (Depth-First)
-- Aleksandar Ignjatovic version.
左边是Residual flow network,一定要亲自画出来。
(a) 初始状态,通过DFS找一条路径作为augmented path; 可见瓶颈是4,然后得到右图。
【注意,为何这里augmented path的选择是如此方式,是否可以变为最短路径作为更好的选择呢】
(b) 同上。
(c) 同上。
(d) 正向的有权边越来越少;然后画出右图(update后的)的residual flow netowork如下。
(e) 这个residual flow netowork貌似已经没有了正向有权边。那么,就可以end了。(d)右就是最终结果,以上是最小割。
良心视频 + 个人补充:https://www.youtube.com/watch?v=Z8gcjuS0Vb8&t=393s
此处注意标号(s,8)表示上一个节点是s node,(s,8)允许通过的流量是8。
增广链的选择,从t node的标号倒推所得。
此时,最大流等于最小割。
Follow an arbitrary path until you reach the sink.
Not guaranteed to terminate.
举例演示其繁琐性:
最后一张 右边的1->2 又变回了 初始状态 value=1。
让我们抽出两张对比:
可见,中间的这条边value=1成了瓶颈。导致了i++类似缓慢的递归过程。
Time complexity
O(KE) K=Maximum Flow, E=Edge
Dependency on the flow
最大流最小割定理
Ref: http://www.cnblogs.com/TreeDream/p/5929429.html
Ref: https://wenku.baidu.com/view/d9c9b9220722192e4536f6e1.html (良心PPT)
割的净流:f = f(2,4)+f(4,3)+f(3,5) = 12+(-4)+11=19 //等于node1的output
割的容量:c(2,4)+c(3,5)=12+11=23
多一些例子如下:
可以计算出对于这两种情况净流f(S,T)仍然等于19。 //等于node1的output
割的净流:f = f(2,4)+f(4,3)+f(5,4)+f(5,6) = 12+(-4)+7+4=19 //等于node1的output
割的容量:c(2,4)+c(5,4)+c(5,6)=12+7+4=23
割的净流:f = f(1,2)+f(3,2)+f(4,3)+f(5,4)+f(5,6) = 11+1+(-4)+7+4=19 //等于node1的output
割的容量:c(1,2)+c(3,2)+c(5,4)+c(5,6)=16+4+7+4=31 (一瞧就不是最小的割)
对于任意一个割的净流f(S,T)一定是小于等于割的容量C(S,T)。那也即是,对于网络的任意一个流f一定是小于等于任意一个割的容量C(S,T)。
而在所有可能的割中,存在一个容量最小的割,我们称其为最小割。(回流最小)
最大流最小割定理 (参考)
对于一个网络流图G=(V,E),其中有源点s和汇点t,那么下面三个条件是等价的:
1. 流f是图G的最大流
2. 残留网络Gf不存在增广路
3. 对于G的某一个割(S,T),此时f = C(S,T)
首先证明1 => 2:
我们利用反证法,假设流f是图G的最大流,但是残留网络中还存在有增广路p,其流量为fp。则我们有流f'=f+fp>f。这与f是最大流产生矛盾。
接着证明2 => 3:
假设残留网络Gf不存在增广路,所以在残留网络Gf中不存在路径从s到达t。
我们定义S集合为:当前残留网络中s能够到达的点。
同时定义T=V-S。//到达不了的点的集合T
此时(S,T)构成一个割(S,T)。且对于任意的u∈S,v∈T,有f(u,v)=c(u,v)。若f(u,v)<c(u,v),则有Gf(u,v)>0,s可以到达v,与v属于T矛盾。
因此有f(S,T)=Σf(u,v)=Σc(u,v)=C(S,T)。
最后证明3 => 1:
由于f的上界为最小割,当f到达割的容量时,显然就已经到达最大值,因此f为最大流。
Edmonds-Karp求最大流算法 (Breadth-First)
核心思想就是不断找增广路,每找到一条增广路,记录增广路中 边流量最少的值 d,
然后在当前找到增广路中
-
- 每一条正向弧减去d,
- 每一条反向弧加上d,
当不再找到增广路的时候,就证明当前的总流量已经是最大流;
至于怎样找增广路。。一致认为BFS是最好的方法了。
英伦良心Tutorial: Edmonds Karp Max Flow Algorithm Tutorial
1. 发现最短路径
Paths:
-
- S->1->2
- S->1->3
- S->3->4
并顺便记录了每个node的child node。
-
- S: 1, 3
- 1: 2, 3
- 3: 4
- 2: t
- 3: 4
- 4: 2, t
可见,最短路径只是根据BFS找,并没有什么特殊的。
2. 处理最短路径
加了反向边,每一条正向弧减去了15。
Total Flow += 15
3. 寻找最短路径(again)
可见,s->3这次也变为了0边。
Total Flow = 15+4, 第二次迭代结果是19.
4. 寻找最短路径(again)
2->t, s->3都变为了0边,那么最短路径的选择结果便有了改变。
Total Flow = 19+7, 第二次迭代结果是26.
Time complexity
All vertices in residual graph increase monotonically.
Total number of iterations is O(VE) V=Vertices, E=Edges
Each iteration O(E)
Therefore total runtime O(VE2)
Maximum matching in bipartite graphs
二分图:简单来说,如果图中点可以被分为两组,并且使得所有边都跨越组的边界,则这就是一个二分图。准确地说:把一个图的顶点划分为两个不相交集 UU 和VV ,使得每一条边都分别连接UU、VV中的顶点。如果存在这样的划分,则此图为一个二分图。二分图的一个等价定义是:不含有「含奇数条边的环」的图。图 1 是一个二分图。为了清晰,我们以后都把它画成图 2 的形式。
匹配:在图论中,一个「匹配」(matching)是一个边的集合,其中任意两条边都没有公共顶点。例如,图 3、图 4 中红色的边就是图 2 的匹配。
我们定义匹配点、匹配边、未匹配点、非匹配边,它们的含义非常显然。例如图 3 中 1、4、5、7 为匹配点,其他顶点为未匹配点;1-5、4-7为匹配边,其他边为非匹配边。
最大匹配:一个图所有匹配中,所含匹配边数最多的匹配,称为这个图的最大匹配。图 4 是一个最大匹配,它包含 4 条匹配边。
完美匹配:如果一个图的某个匹配中,所有的顶点都是匹配点,那么它就是一个完美匹配。图 4 是一个完美匹配。显然,完美匹配一定是最大匹配(完美匹配的任何一个点都已经匹配,添加一条新的匹配边一定会与已有的匹配边冲突)。但并非每个图都存在完美匹配。换一个说法:最多有多少互相喜欢的男孩/女孩可以配对儿?这就是最大匹配问题。
一、匈牙利算法
求解最大匹配问题的一个算法是匈牙利算法,下面讲的概念都为这个算法服务。
交替路:从一个未匹配点出发,依次经过非匹配边、匹配边、非匹配边…形成的路径叫交替路。
增广路:从一个未匹配点出发,走交替路,如果途径另一个未匹配点(出发的点不算),则这条交替路称为增广路(agumenting path)。例如,图 5 中的一条增广路如图 6 所示(图中的匹配点均用红色标出):
增广路有一个重要特点:非匹配边比匹配边多一条。因此,研究增广路的意义是改进匹配。只要把增广路中的匹配边和非匹配边的身份交换即可。由于中间的匹配节点不存在其他相连的匹配边,所以这样做不会破坏匹配的性质。交换后,图中的匹配边数目比原来多了 1 条。
我们可以通过不停地找增广路来增加匹配中的匹配边和匹配点。找不到增广路时,达到最大匹配(这是增广路定理)。
那么,如何在某一时刻/状态 找增广路呢?
匈牙利树一般由 BFS 构造(类似于 BFS 树)。从一个未匹配点出发运行 BFS(唯一的限制是,必须走交替路),直到不能再扩展为止。例如,由图 7,可以得到如图 8 的一棵 BFS 树:
这棵树存在一个叶子节点为非匹配点(7 号),但是匈牙利树要求所有叶子节点均为匹配点,因此这不是一棵匈牙利树。
(因为node7,所以不是匈牙利树)
如果原图中根本不含 7 号节点,那么从 2 号节点出发就会得到一棵匈牙利树。这种情况如图 9 所示(顺便说一句,图 8 中根节点 2 到非匹配叶子节点 7 显然是一条增广路,沿这条增广路扩充后将得到一个完美匹配)。
匈牙利算法的要点如下
-
从左边第 1 个顶点开始,挑选未匹配点进行搜索,寻找增广路。
- 如果经过一个未匹配点,说明寻找成功。更新路径信息,匹配边数 +1,停止搜索。
- 如果一直没有找到增广路,则不再从这个点开始搜索。事实上,此时搜索后会形成一棵匈牙利树(Fig.9)。我们可以永久性地把它从图中删去,而不影响结果。
-
由于找到增广路之后需要沿着路径更新匹配,所以我们需要一个结构来记录路径上的点。DFS 版本通过函数调用隐式地使用一个栈,而 BFS 版本使用
prev
数组。
过程演示
先从1出发,找增广路,找到1->A 这条路,标记并记录。
从2出发,找到2->B这条路,标记并记录。
从3开始找,发现3所连接边全部被占用,这时进行一个神奇的操作:
-
- 从三开始找一条增广路,3 -> A -> 1 -> B -> 2 -> C
- 下划线要出现在两端(增广路的性质)
- 有增光路,绿线所示
Finally,在图中将有两种颜色的边删去,留下绿色的边。
性能比较
两个版本的时间复杂度均为O(V⋅E)。DFS 的优点是思路清晰、代码量少,但是性能不如 BFS。
我测试了两种算法的性能。对于稀疏图,BFS 版本明显快于 DFS 版本;而对于稠密图两者则不相上下。
在完全随机数据 9000 个顶点 4,0000 条边时前者领先后者大约 97.6%,9000 个顶点 100,0000 条边时前者领先后者 8.6%, 而达到 500,0000 条边时 BFS 仅领先 0.85%。
二、最大流算法
然而,我们也可以把这个问题当作是最大流问题,如下:(匈牙利算法实际上是对最大流算法的一种改进,提高了效率)
中间部分的节点容量设置为1。
计算二分图最大匹配除了匈牙利算法(Hungarian Algorithm),还可以用最大流(Maximal Flow)。
Link: https://www.youtube.com/watch?v=x2BdRml5lmc
Notice:设,所有Edges的权值都是1!
巧妙利用min cuts
(1) 实验与仪器,以获得最大受益为目的挑实验来做
E1 (10) --> I1 (5), I2 (6)
E2 (25) --> I2 (6), I3 (7)
因为:最大净收益 = 所有实验收益 - 相应实验方案割的容量
所以:最大净收益 = 所有实验收益 - 最大流
注意 cut的位置!以下稍作权值调整再做一次练习:
最大净收益(9) = 所有实验收益(20+25) - 最大流(5+6+25)
(2) 断开连接,容量代价最小
任务:以最小的断开link的cost分离attacher and victims的PC.
Sol:找到两者的最大流,即是最小割(所有正向割边的容量和称为割的容量,那个最小容量的割),断在这些属于min cuts的edge即可。
(3) 伤员分配问题
任务:左边building里的伤员需要运送到距离20KM以内的医院。
Sol:左边是building内的伤员数量,右边对应医院的容纳量。
(4) 宴席人员分配问题
任务:左边家庭里的成员坐在安排的宴席餐桌,要求家庭成员不能坐在一起。
Sol:添加右边的到T Node的流,权值即是餐桌能容纳的人数。