1. 程式人生 > >網路流 (一) 最大流的原理圖解

網路流 (一) 最大流的原理圖解

嗯…於是學一波網路流罷
之前學過一波,不過失敗了orz
希望這次能學成功(x)

建模

想象一下,你在排程貨車運輸(不是最小生成樹+LCA那道題放心吧),但是有些橋是有載重限制的。比如下圖:

綠色的邊表示橋,上面的數字表示載重。
老闆打算從A到G。
顯然,作為一個老闆,超載是不合適的,姑且不論貨物安全,還有可能受到法律懲罰!那麼,最多能一次載多少貨物呢?

在解決這個問題之前,我們先為這一模型定義一些概念。
出發點成為源點,也就是A,顯然,該點入度為0
終點稱為匯點,也就是G,顯然,該點出度為0
而每條邊上的最大載重稱為容量,例如CD邊的容量是6。引入符號c[i,j]

表示從節點i到節點j的容量;在實際問題中,一條邊不一定必須達到載重,一個載重6t的路放4t的貨車運輸,那麼我們就稱4是這條路的流量。引入符號f[i,j]表示節點i到節點j的容量。
顯然,由於不可能超過限重,f[i,j]<=c[i,j]恆成立。
並且,貨物顯然不能損失吧,所以源點和匯點的流量是相同的,對於所有點,流入的流量和流出是相同的。

建模完成,接下來就是求解了。

增廣

首先,我們定義一種合理的載重方法為可行流。比如下圖:

黃色的代表一個可行流,其中數字第一個表示容量,第二個為流量。
我們知道,零流就是一個典型代表——畢竟,它並不會超過容量。

然後,我們就可以開始增廣操作了。其實增廣是一種自然而然的想法:假定路徑s

是當前的流,並且,這條路徑還可以拓展,那麼我們就試圖去塞進去更多的流。
這樣說不是很明白,舉個栗子(為了方便我畫圖起見我們姑且換一個圖)來演示一下過程好了。

好的,現在先yy一下圖中存在0流。這裡我們寫了一個東西叫做剩餘,表示的是當前這中方式下還有多少流能夠匯入。
那麼,增廣開始。不妨採用BFS的思想,第一次找到了路徑ABC
我們發現,這條路線上最多能增廣2單位的流,所以我們將流注入。

圖中的n/m表示/
此時變成了這種樣子。然後我們仍然BFS,發現了ABDC這條路徑。這條路徑上最多能增廣一個單位的流,於是增長之。

沒毛病!然後再去增廣ADC這條路,結果發現,可以增長三個單位的流。增長之:

掃視一遍,發現未有能增廣者,結束增廣。
完成了,可喜可賀。由於源點和匯點的流量相同,我們知道,最大流就是6個單位。為之四顧,為之躊躇滿志。
但是,在你躍躍欲試去寫網路流模板之前,不得不先潑一瓢涼水——
如果此時BFS沒有去增廣ABC,而是先增廣了ABDC呢?這個時候,我們再來看看情況:

(上面的CD應該剩餘1,寫錯了)
繼續:

驚悚的事情發生了。
沒錯,之後沒有辦法增廣了,但是!此時我們求出的最大流是4!

這是否證明我們的貪心思路是錯的呢?
機制的珂學家們引入了下面的概念,完美的迴避掉了這個致命的漏洞——

反向弧

既然這麼貪不一定是對的,那麼我們就可以引入一種後悔機制,來提供反悔的機會。
怎麼搞呢?我們可以每增廣一次,就建立一條反向邊。
仍然是上面的例子,在對ABCD增廣之後如下圖。

注意,這裡的ABCD並不是虛點,而是ABCD本身,這裡只是為了方便畫圖和觀察所以分開畫了。
然後呢——我們發現,ADBC變得可增廣了,也就是,AD,DB,BC這條。那麼我們對它進行增廣之後:

這個圖可能稍微有些亂,紅色的是我們增廣的東西,綠色是增廣建的反向邊。
當然,繼續掃一遍,發現ADC還可以增廣,於是最後的結果:

沒有可以增廣的了——完成DAZE!
所以最終的結果就是6。
那麼可能有人會問了——上面我們走了ADBC這條原本不應該存在的路,意義是什麼呢?其實,這相當我們把原本貪心走BD這條路的流逼了回去,轉而走BC

上面就是一切網路流問題的基礎,也是最大流的原理。下一篇文章,我們考慮其實現辦法。

參考Blog