前言
最大流問題是網絡優化中典型的問題,用形象的語言來描述就是在滿足容量約束的前提下將盡可能多的流從源節點(始點)到匯節點(終點)。解決此問題的經典方法很多,本文介紹廣為人熟知的Ford-Fulkerson算法,來解決最大流問題。盡管網上關於此問題的文章多如牛毛,但筆者希望結合自己學習過程中對算法的理解,給予算法最清晰的介紹,希望對大家有幫助。(筆者曾嘗試使用java來實現,但最終因用java實現和使用圖太麻煩了,又不想重新用python來實現,故放棄,以后的實現估計會采用python而不是java,java畢竟不適合寫圖相關算法)。
一、知識准備
學習此算法前建議讀者學習網絡流相關知識和基本概念,一些需要了解的概念和知識先羅列如下
1、可增廣路
2、最大流最小割定理
3、圖的割集
4、飽和割集
5、可行流向量
6、節點的散度
7、前向后向邊
二、可增廣路搜索算法
對於k=0,1,..., 給定Tk, 若Tk為空集或者t∈Tk則算法終止。否則,令
Tk+1 = { Tk | 存在節點m∈Tk, 以及邊(m, n) 滿足xmn < cmn, 或者邊(n, m) 滿足 bnm<xnm},
(其中節點數為n,bnm和cnm為邊mn容量的最小和最大值,xmn為邊的流量, Tk+1集合的節點不能屬於T其他集合節點)
上式的含義即為Tk+1為在集合Tk中所有節點滿足可增廣路條件的相鄰的節點組成的集合,當然,這些節點不能包括已經存在其他T集合中的節點。
當算法終止時,最后一個集合Tk有兩種情況(假設s為起點, t為終點):
(1) 最后一個集合Tk包含t,此時從t向前標記即可構造從s到t的簡單可增廣路。即從T1,T2,T3,....,Tk中每個取一個節點的路
(2)最后一個集合Tk為空集,此時得到一個分離s和t的飽和割集
現舉例說明,如下圖(容量下限(最小值),流量,容量上限(最大值))標在每條邊旁
1為源點,6為匯點。開始T0只有1個節點,就是節點1。接下來從和T0中節點,也就是節點1相鄰的節點2和3開始。邊12,流量為0,為前向邊,滿足流量小於容量上限;邊13也為前向邊,滿足流量小於上限(0 < 1)。所以節點2和節點3組成T1。然后考慮以節點2和節點3來尋找T2,原理和前面一致,其中邊35,為后向邊,流量為2,大於容量下限1,所以節點5添加到T2,。到最后T3,集合包含6,終止。此時增廣路為(1,3,5,6)。
現在看另一圖,只是把邊56的流量改為了0,此時后向邊56不滿足流量大於下限,所以T3為空集。此時存在飽和割集{S, N-S}分離1和6,其中S = {1,2,3,4,5},是所有集合Tk的並集
三、Ford-Fulkerson算法
在了解了可增廣路搜索算法后,現在正式進入本文的主題,Ford-Fulkerson算法。該算法在每次迭代中改進原費用(s的散度),因此是原費用改進型算法。算法思想是,給定可行流向量x(即滿足容量可行且除了s和t之外節點的散度為零,即節點流入和流出相等,的流向量),以及對於x可增廣的從s到t的路P,就可以增進P的前向邊(i,j)的流量並減小P的后向邊(i,j)的流量。流量增量的最大值為
δ = min{ {cij - xij | (i, j) ∈ P+}, {xij - bij | (i, j) ∈ P-} }, 其中P+是P的前向邊的集合, P-是P的后向邊的集合。
由此得到的流向量y由
yij = :xij + δ 如果(i, j) ∈ P+,
xij - δ 如果(i,j) ∈P-,
算法的迭代步驟
使用可增廣路搜索算法實現下述兩種情況之一:
(1)、得到將s和t分離的飽和割集
(2)、得到從s到t的x的可增廣路P。
在情況1下,算法終止;此時的流向量即為所求。在情況2下,沿着P進行增廣,並進行下一次迭代。每次增廣時,算法通過增廣增量δ改進原費用(s的散度)。現在以例子說明
初始圖,如圖1。搜索到增廣路(1, 2, 5),路12和路25為正向邊,求δ=min{ c12- x12, c25- x25}為1。如圖2
圖1
圖2
對增廣路中的所有邊x12和x25使用δ改進費用,因為均為正向邊,有新的流量為x12=0 + 1 = 1, x25= 0 + 1 = 1。費用改進后如圖3。費用改進后繼續尋找增廣路,如圖4。
圖3
圖4
迭代
圖5
圖6
迭代
圖7
圖8
在最后一次費用改進后,已找不到增廣路。其中T0包含節點1,T1包含節點2,T3包含節點3,因此飽和割集為[{1, 2, 3}, {4, 5}]。割集容量即為最大容量,即5=c24 + c34 + c25 = 5
圖9
四、
在學習此算法時,筆者已經盡力描述算法的主要內容了。由於需要有一些圖論的知識作為輔助,加上算法有些復雜,所以結合文中例子效果會更好。
五、知識相關