【转载】网络流和最小费用流
這段時間復習了下網絡流模型,感覺比以前的理解有了長足進展,雖然我知道這東西難就難在建模上,而它的算法本身其實難度不大,但我還是決定說一些我的理解,畢竟理解了本質的東西運用起來才會更靈活。
最大流的求解一般有兩類算法(用費用流附帶求出的不列入考慮范圍),就是增廣路(FF)系列和預流推進(PF)系列。在很多地方都看到推薦使用后者,因為效率更高,但其實不然,今天我這篇文章就是要重點介紹前者,相信大家看了過后也會喜歡它的。
首先,那些一大堆的關于網絡的定義我就不說了,不明白的就先去看看書好了,我們約定S是源,T是匯,G表示殘余圖。增廣路算法其實相當好理解,因為只要在G中存在一條從S到T的通路,那沿這條路走,流量一定還可以增加,所以當求得最大流的時候一定不存在S-T通路。 那反過來成立么?顯然,因為我們有最小割最大流定理,所以由此就得到了FF算法的基本方法:
不斷的在G中找一條S-T通路,然后沿其增廣,直到無法找到。
至于用什么方法去找一條路,那就隨便了,BFS,DFS,甚至A*,你可以把你能想到的方法試個遍,看看哪個合胃口就用哪個吧。
注意那個用BFS的方法,它的官方名字叫Edmonds-Karp(EK),它和我們后面要說的費用流有很大的關系,這里先簡單提示一下。
那這方法的復雜度呢?如果是整數流(事實上分數流的話可以構造數據讓FF無法停止,這時候必須用到PF的方法),顯然每次至少增廣1的流量,每次查找O(E)條邊,增廣要O(V)的時間,因此復雜度為O((m+n)*U),U是最大流。
聽上去很恐怖,其實EK算法的一個稍微精確的上界是O(VE^2),還是很夸張。
那么,怎樣才能跑得更快呢?想想,每次用BFS求出的Shortest Path Tree(SPT),我們只關心了S-T最短路,其實還有很多信息沒有利用到,那么如果求一次SPT,我們可以增廣多次的話,是不是會好些??
于是,引入層次圖的概念,說通俗點,就是求G的SPT,把G中每個頂點的距離S的標號d給求出來,保留所有的s->t中d[s]+1=d[t]的邊(注意可以不是SPT中的邊),最后再在層次圖中用多次BFS把所有的增廣路求出來增廣。
這樣看似更快了,其實沒有,因為分析復雜度依然是O(VE^2),沒有改進,而且我們還要寫更多的代碼。那瓶頸在哪里呢?對了,就是那步在層次圖中用BFS來找增廣路。其實更好的方法是用DFS,一次就可以把所有的增廣路求出來,具體做法如下:
從S開始做DFS,一旦發現一條增廣路,于是就沿其增廣,然后DFS回退到離S最近的一條滿流的邊的起點處,并在圖中刪除該邊的終點,繼續搜索。
上面的算法就是著名的Dinic算法,它的復雜度為O(EV^2)(注意,看清楚2的位置),對于密圖有不小的改進。
事實上,我非常推薦在各類比賽中用Dinic算法,因為它實現簡單,而且實際運行速度快,比起PF算法好寫太多了!!
那單純的說好還是不行,要拿出依據來。關于PF算法的理論在本文中就不詳細敘述了,可能以后我會專門介紹它。一般的PF實現是O(V^4)的,而Relable-To-Front是O(V^3),高標推進據稱是O(V^2*sqrt(E)),雖然它實際中確實比較快,但是PF的算法都有一個缺點,就是必須加啟發優化,至少是Gap優化,否則實際中會比較慢。因此,寫一個PF的代碼自然就比較多了。
上面基本上把最大流的算法盤點了一下,下面還是說一下最小費用流問題。
如果一個網絡的邊不僅有容量,還有單位流量費用的話,那我們自然想在求得最大流的同時,使總費用更低。因此,最小費用流實際上是一個更通用的模型,因而其應用也更廣。
令 人驚奇的是,解決該問題可以不需要先求出最大流來(當然也有方法需要),所以如果你不想記那么多算法的話只知道該算法也沒問題。因為費用流的算法如果說復 雜的話可以把線性規劃扯近來,所以先羅列下幾個基本方法,然后介紹最簡單的一種,其它的如果你有興趣可以自己找文章來看:
1、 連續最短路算法(Successive Shortest Path);
2、 消圈算法(Cycle Canceling);
3、 原始對偶算法(Primal Dual);
4、 網絡單純形(Network Simplex)。
后面的兩個分別是前面兩個的高級優化版本,其基本的優化思路就是和我們上面論述的優化增廣類似,就是希望每次都能做幾次運算,不要一次用了就丟棄了。最好用的就是第一個方法,方法二在Algorithm in C圖論部分有詳細論述,我就不說了。
還記得剛才我說的EK么?其實如果我們用Dijkstra在G中找S-T最短路,而不是BFS的話,是不是就解決問題了呢?是的,完全正確,我把證明留給大家想,其實很簡單。
但是,有個問題,在G中存在負權的邊,用Dijkstra是不是不行了呢?非要用Bellman-Ford么?其實不然,只要原圖中沒有負環存在(有的話不存在最小費用),那么我們可以利用Johnson算法的重賦權技術把所有的邊先變成正權,然后以后每次增廣后再維護一下不就可以用高效的Dijkstra了?算法如下:
用Bellman-Ford求各點到S的高度標號d[];
以后每求一次最短路,設標號為pi[],那么執行:
For i=1 to v do
d[v]+=pi[v]
這樣一來,標號就一直合法了(具體證明還是留給大家吧)。
好了,說這么多理論,下篇文章將會說幾個具體的題目,加深對算法的理解。
?
轉載于:https://www.cnblogs.com/zen_chou/archive/2009/07/16/1525185.html
總結
以上是生活随笔為你收集整理的【转载】网络流和最小费用流的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 首款Redmi平板来了:7800mAh大
- 下一篇: PB中函数测试遇到的问题