1. 程式人生 > >最大流問題與Ford-Fulkerson演算法介紹

最大流問題與Ford-Fulkerson演算法介紹

背景

  1. 我們有圖 G=(V, E),V是頂點的集合,E是邊的集合。
  2. 圖中邊的權重都為非負數 (滿足1,2兩點有時稱之為流網路)。
  3. 對於這個圖G,有兩個頂點很重要,一個是源頭s,一個是匯聚點t,我們想考慮的是從源頭s流向匯聚點t的

我們想要解決的問題:在一個流裡,有著每條邊的運載能力限制,我最多能從源頭運輸多少數量到目的地。

那麼什麼是流呢?

流的定義

定義:直觀來說,流就像它的名字一樣,從源頭s運送一些“東西”到匯聚點t,比如下水道系統運輸水流,公路網路運輸車流。

流具有三個需要注意的點!!

  1. 對於圖中非s和t的普通結點,流進量等於
    流出量
  2. 我們非常關心總運輸流量,比如這個下水道系統,究竟從s點到t點最多能運輸多少立方米的水?我們把它記成|f|,這個|f|極其重要,是我們研究的目的所在。
  3. 當然,每條邊是有運輸上限的,就像某條公路車流是有上限的一樣,若運輸量無窮無盡,我們的研究也就沒有意義了。我們將從u點到v點的運輸上限,或者說是運載能力記為c(u,v)。對於從u點到v點的流量,記作f(u,v)。顯然對所有邊(u,v)我們有f(u,v)<=c(u,v)。

研究目的:最大化|f| (|f|的定義在上面第二點),在一個流網路裡,有著每條邊的運載能力限制,我最多能從s運輸多少東西到t。

總結一下,流可以看成一個圖,滿足兩個條件:
1.除了s和t,流入等於流出。
2.所有邊的流量f小於運載量c。(flow < capacity)
讓我們來看一個流的例子:
在這裡插入圖片描述


此圖中除了s和t,流入等於流出,每條邊的流量小於運載上限(比如10/20,10是流量,20是上限)。

那麼這個圖中我們關心的總運輸量是多少呢?

是10!直觀來想,從s流出10的流量,在複雜網路中經過一系列流入等於流出的操作,最終匯入t,這就是總流量的定義:從s運輸到t的流量數。

所以提及總流量,我們盯著s看就好了。

回憶起我們的研究目的,其實就是想尋找一個最大的|f|,此例中|f|=10,還能更大嗎?

這就是最大流問題:尋找滿足運載上限的最大流。
為了解決這個問題,我們引進割,那麼割是什麼呢?

割的定義

割說白了,就是對於點的分割,有了流的基礎,我們直接看例子。
在這裡插入圖片描述
左右兩個黃色的區域,說白了就是點的集合,正式一點來說,我們把它記成(S,S’),比如左黃區域為S(源頭s在S裡),右黃區域為S’(目的地t在S’裡)。
割很隨意,但是有個要求,就是s和t要屬於不同的

區域!

對於割,我們只關心一個事情:從S到S’的總運載上限cap(S,S’),就是割的容量。
在這裡,我們不關心從S到S’的流,不關心從S’到S的總運載上限,比如這個例子裡,cap(S,S’)=5+10=15,而不是等於5+10+15=30。

這個定義還挺死板的,總結一下,割形成了兩個區域,我們關心的是兩個區域之間“橋樑”的運載量之和,橋樑的方向一定是一致的,比如都是從S到S’的方向。

在我們知道了所有割的容量後,我們最想知道的是這其中最小的那個容量,即最小割的容量。

說完了割,還是沒有說到為什麼這個概念的引進可以解決最大流問題,我們接下來看看最大流最小割的關係。

最大流最小割

我們回憶一下,流是一個邊權重非負的圖,流入等於流出。割是把流分成兩個區域,s和t分佈於不同的區域。

最小割大於等於最大流,因為只有這樣,流才能順利從一個區域運輸到另一個區域(想想流要通過從S到S’的橋樑)。

換言之,對於任意一個割(S,S’),最大流都要小於等於cap(S,S’),因為我研究的是從源頭s到t,可以想成是從S區域到S’區域,那麼我運輸的流量肯定要小於等於橋樑的總限制。

所以我們有了第一個關係式:max|f| <= cap(S,S’)

關鍵的來了,這個關係式的上界取等發生在(S,S’)是最小割。這個被稱為最大流最小割定理,所以我們完成了轉化,將尋找最大流問題轉化為了尋找最小割的問題。

那麼如何找到最小割呢?
首先引進兩個術語:saturate和avoid,這兩個詞都是形容流f對邊e)的。還是很形象的,一個充滿一個避開。
f saturates e: 即f(e) = c(e),就是流量充滿了運載上限,比如10/10。
f avoids e: f(e) = 0
有這兩個術語後,我們有方法如下:
f是一個流,(S, S’)是一個割,如果f充滿了從S到S’的每一座橋,避開了從S’到S的每一座橋,則f是一個最大流,(S, S’)是一個最小割。
那麼為什麼最大流等於最小割的總運載上限呢?(關於這個問題的解答,還是開一篇新的文章吧)
先不考慮為什麼,我們先介紹如何找到最大流的演算法,即Ford-Fulkerson演算法。

Ford-Fulkerson

首先這個演算法有個重要的工具:殘存網路。殘存網路其實就是具有殘存容量的圖。演算法導論上有個普遍公式來定義殘存容量:
在這裡插入圖片描述
翻譯一下公式,說明的就是對於兩點間的殘存容量定義為:
1.如果這兩點連線原來就是原圖的邊,那麼它的殘存容量等於運載上限-運輸流量。
2.如果這兩點的反向連線是原圖的邊,那麼它的殘存容量等於那條邊的運輸流量。
3.其他情況是0,當做沒連通。

很不直觀呀,所以我們結合例子來說明說明殘存容量以及殘存網路。
首先先明確一下,在殘存網路中我們只關心運載上限(就是殘存容量啦)。
在這裡插入圖片描述
這個例子中,左邊是原圖,右邊是殘存網路。
我們先來看源頭s和它右上角的結點a(就是10/20相連的那兩點),為什麼轉換成殘存網路中形成了一個10和10的圈?
我們回到不直觀的公式定義,cf(s,a)應該是等於20-10的,因為(s,a)是原圖的邊。那麼cf(a,s)是多少呢,因為這個的反向連線在原圖裡是(s,a)是邊,所以cf(a,s) = f(s,a) = 10
那麼直觀來說怎麼生成殘存網路呢,原圖的每條邊都需要拆解,比如考慮那條5/15的從b指向t的邊,我們畫在殘存網路中就是生成一條15-5容量的邊,從b指向t,然後生成一個5的邊,從t指向b。(不用為b是哪個點感到困惑,其實就是例子中t左上角的那個點)如果發現容量為0的邊,就不用畫在殘存網路裡了。
請在例子裡練練手,找上幾個殘存容量你就掌握瞭如何生成殘存網路的方法了。

接下來我們看看殘存網路對我們的幫助
1.殘存網路中沒有從s到t的路徑時,最大流等於最小割容量。
2.殘存網路中有從s到t的路徑時,最大流不等於最小割容量。
關於以上兩點的證明用到了割,會在新一篇文章說到。

如果是情況2:我們考慮路徑P:s->v0->v1->v2->… ->vn ->t,把它叫增廣路徑。在增廣路徑中找最小的cf,記作F。在這種殘存網路還有路徑從s通向t的情況中,我們沒有做到最好,我們要把F納入我們對於流的更新,直到找不到增廣路徑為止,更新方法如下:
在這裡插入圖片描述
做了這麼多鋪墊,又是殘存網路,又是對流的更新,接下來介紹Ford-Fulkerson演算法。
我們從0流量出發(此時殘存網路就是原圖),找到增廣路徑(注意增廣路徑一定是在殘存網路裡找),接著把流更新,直到殘存網路中沒有增廣路徑(就是沒有路徑從s到t啦)為止。下面我們還是看例子:
在這裡插入圖片描述
在示例中,左邊是殘存網路,右邊是更新後的原圖。一開始(a)的殘存網路就是原圖,我們找了條增廣路徑(粗線),找到了最小的cf,就是4,然後把4引入了原圖的流更新。
從(a)更新後的原圖,我們可以畫出它的殘存網路,於是我們來到了(b),還是老步驟,找增廣路徑,找最小的cf,以它來更新原圖(從(a)的右圖到(b)的右圖)。強烈推薦從c往後自己將這個過程找完,花幾分鐘找完你就記住了Ford-Fulkerson演算法。答案如下,增廣路徑選取不同,過程可能不一樣,但是最大流都是一樣的,就是23:
在這裡插入圖片描述
在(f)中,我們再也無法找到增廣路徑,所以我們的演算法結束,這是我們的最大流。

總結一下,我們要解決的問題是在一個有運載限制的網路系統中,從s到t最多能運輸多少流量。我們引進了流,割的概念,將我們研究的問題轉化為尋找最大流。
如何找最大流?採用Ford-Fulkerson演算法,從0流量開始,生成殘存網路,找到增廣路徑,從增廣路徑中找到最小的殘存容量F,用F來更新原圖,得到新的原圖後,迴圈上述過程直到殘存網路中找不到增廣路徑。此時的原圖中,就有了我們想求的最大流。
那麼我們引進割是為什麼呢,是為了證明Ford-Fulkerson演算法的正確性。這個可以開一篇新的文章來說,想要理解Ford-Fulkerson的執行過程看這篇文章就夠了,如果想知道為什麼Ford-Fulkerson可以找到最大流,那麼請看下一篇部落格。