1. 程式人生 > >C++程式碼實現Ford-Fulkerson方法Edmonds Karp演算法解決最大流問題

C++程式碼實現Ford-Fulkerson方法Edmonds Karp演算法解決最大流問題

最近又複習了下最大流問題,每次看這部分的內容都會有新的收穫。可以說最大流問題的資料網上一搜一大把,根本沒有必要自己寫;但是大部分資料上的專業術語太多了,初學很難理解,至少我當年學這部分的時候前幾次就沒有看懂。所以我準備備份一點個人的理解。

圖-1

  如圖-1所示,在這個運輸網路中,源點S和匯點T分別是1,7,各邊的容量為C(u,v)。圖中紅色虛線所示就是一個可行流。標準圖示法如圖-2所示:

其中p(u,v) / c(u,v)分別表示該邊的實際流量與最大容量。

 

關於最大流

  熟悉了什麼是網路流,最大流也就很好理解了。就是對於任意的u∈V-{s},使得p(s,u)的和達到最大。上面的運輸網路中,最大流如圖-3所示:MaxFlow=p(1,2)+p(1,3)=2+1=3。

  在介紹最大流問題之前,先介紹幾個概念:殘餘網路,增廣路徑,反向弧,最大流定理以及求最大流的Ford-Fulkerson方法。

殘餘網路 增廣路徑 反向弧

  
觀察下圖-4,這種狀態下它的殘餘網路如圖-5所示:

  也許現在你已經知道什麼是殘餘網路了,對於已經找到一條從S 到T的路徑的網路中,只要在這條路徑上,把C(u,v)的值更新為C(u,v)-P(u,v),並且新增反向弧C(v,u)。對應的增廣路徑Path為殘留網路上從S到T的一條簡單路徑。圖-4中1,2,4,7就是一條增廣路徑,當然還有1,3,4,7。

  此外在未做任何操作之前,原始的有向圖也是一個殘餘網路,它僅僅是未做任何更新而已。

 

最大流定理

  如果殘留網路上找不到增廣路徑,則當前流為最大流;反之,如果當前流不為最大流,則一定有增廣路徑。

 

Ford-Fulkerson方法

  介紹完上面的概念之後,便可以用Ford-Fulkerson方法求最大流了。為什麼叫Ford-Fulkerson方法而不是演算法,原因在於可以用多種方式實現這一方法,方式並不唯一。下面介紹一種基於廣度優先搜尋(BFS)來計算增廣路徑P的演算法:Edmonds-Karp演算法。

  演算法流程如下:

  設佇列Q:儲存當前未訪問的節點,隊首節點出隊後,成為已檢查的標點;

  Path陣列:儲存當前已訪問過的節點的增廣路徑;

  Flow陣列:儲存一次BFS遍歷之後流的可改進量;

  Repeat:

    Path清空;

    源點S進入Path和Q,Path[S]<-0,Flow[S]<-+∞;

    While Q非空 and 匯點T未訪問 do

        Begin

            隊首頂點u出對;

            For每一條從u出發的弧(u,v) do

                If v未訪問 and 弧(u,v) 的流量可改進;

                Then Flow[v]<-min(Flow[u],c[u][v]) and v入隊 and Path[v]<-u;

    End while

    If(匯點T已訪問)

    Then 從匯點T沿著Path構造殘餘網路;

  Until 匯點T未被訪問

 

應用例項

  這是一道最大流的入門題,題目如下:

Description

  Every time it rains on Farmer John's fields, a pond forms over Bessie's favorite clover patch. This means that the clover is covered by water for awhile and takes quite a long time to regrow. Thus, Farmer John has built a set of drainage ditches so that Bessie's clover patch is never covered in water. Instead, the water is drained to a nearby stream. Being an ace engineer, Farmer John has also installed regulators at the beginning of each ditch, so he can control at what rate water flows into that ditch. 
Farmer John knows not only how many gallons of water each ditch can transport per minute but also the exact layout of the ditches, which feed out of the pond and into each other and stream in a potentially complex network. 
Given all this information, determine the maximum rate at which water can be transported out of the pond and into the stream. For any given ditch, water flows in only one direction, but there might be a way that water can flow in a circle.

Input

  The input includes several cases. For each case, the first line contains two space-separated integers, N (0 <= N <= 200) and M (2 <= M <= 200). N is the number of ditches that Farmer John has dug. M is the number of intersections points for those ditches. Intersection 1 is the pond. Intersection point M is the stream. Each of the following N lines contains three integers, Si, Ei, and Ci. Si and Ei (1 <= Si, Ei <= M) designate the intersections between which this ditch flows. Water will flow through this ditch from Si to Ei. Ci (0 <= Ci <= 10,000,000) is the maximum rate at which water will flow through the ditch.

Output

  For each case, output a single integer, the maximum rate at which water may emptied from the pond.

Sample Input

5 4

1 2 40

1 4 20

2 4 20

2 3 30

3 4 10

Sample Output

50

 1 #include <iostream>
 2 #include <queue>
 3 using namespace std;
 4 
 5 const int N = 210;
 6 const int INF = 0x7FFFFFFF;
 7 int n,m,map[N][N],path[N],flow[N],start,end;
 8 queue<int> q;
 9 
10 int bfs(){
11     int i,t;
12     while(!q.empty()) q.pop();
13     memset(path,-1,sizeof(path));
14     path[start]=0,flow[start]=INF;
15     q.push(start);
16     while(!q.empty()){
17         t=q.front();
18         q.pop();
19         if(t==end) break;
20         for(i=1;i<=m;i++){
21             if(i!=start && path[i]==-1 && map[t][i]){
22                 flow[i]=flow[t]<map[t][i]?flow[t]:map[t][i];
23                 q.push(i);
24                 path[i]=t;
25             }
26         }
27     }
28     if(path[end]==-1) return -1;
29     return flow[m];                   //一次遍歷之後的流量增量30 }
31 int Edmonds_Karp(){
32     int max_flow=0,step,now,pre;
33     while((step=bfs())!=-1){          //找不到增路徑時退出34         max_flow+=step;
35         now=end;
36         while(now!=start){
37             pre=path[now];
38             map[pre][now]-=step;      //更新正向邊的實際容量39             map[now][pre]+=step;      //新增反向邊40             now=pre;
41         }
42     }
43     return max_flow;
44 }
45 int main(){
46     int i,u,v,cost;
47     while(scanf("%d %d",&n,&m)!=EOF){
48         memset(map,0,sizeof(map));
49         for(i=0;i<n;i++){
50             scanf("%d %d %d",&u,&v,&cost);
51             map[u][v]+=cost;           //not just only one input52         }
53         start=1,end=m;
54         printf("%d\n",Edmonds_Karp());
55     }
56     return 0;
57 }
58 

相關推薦

C++程式碼實現Ford-Fulkerson方法Edmonds Karp演算法解決問題

最近又複習了下最大流問題,每次看這部分的內容都會有新的收穫。可以說最大流問題的資料網上一搜一大把,根本沒有必要自己寫;但是大部分資料上的專業術語太多了,初學很難理解,至少我當年學這部分的時候前幾次就沒有看懂。所以我準備備份一點個人的理解。 圖-1   如圖-1所示,在這個運輸網路中,源點S和匯點T分別是1

演算法導論—(Edmonds-Karp演算法)

華電北風吹 天津大學認知計算與應用重點實驗室 2016-07-20 有向圖的最大流演算法程式碼模板。利用廣度優先搜尋尋找殘量網路增廣路。 參考程式碼: #include <iostr

7. 網路演算法--Ford-Fulkerson方法及其多種實現

public  class Network {      //     私有成員變數     // 頂點連結串列陣列,陣列的每個元素對應於     // 與頂點相連的所有頂點形成的連結串列     

Ford-Fulkerson演算法C++實現

本文主要講解最大流問題的Ford-Fulkerson解法。可是說這是一種方法,而不是演算法,因為它包含具有不同執行時間的幾種實現。該方法依賴於三種重要思想:殘留網路,增廣路徑和割。 一、殘留網路 顧名思義,殘留網路是指給定網路和一個流,其對應還可以容納的流組成的網路。具體說來,就是假定一個網

Ford-Fulkerson方法詳解及實現

最大流問題常常出現在物流配送中,可以規約為以下的圖問題。最大流問題中,圖中兩個頂點之間不能同時存在一對相反方向的邊。 邊上的數字為該條邊的容量,即在該條邊上流過的量的上限值。最大流問題就是在滿足容量限制條件下,使從起點s到終點t的流量達到最大。在介紹解決最大流問題的For

問題——Ford-Fulkerson方法的java實現

使用的圖的資料結構是鄰邊雜湊表,見Graph原始碼。 package algorithm; import java.util.LinkedList; import java.util.List; import java.util.Queue; import

網路演算法--Ford-Fulkerson方法及其多種實現

public class Network {     //    私有成員變數     //頂點連結串列陣列,陣列的每個元素對應於     //與頂點相連的所有頂點形成的連結串列    private NetworkNodeLList[] vertexList;     //邊的個數和頂點的個數   

C程式碼實現從FTP上下載檔案

從FTP上下載檔案這個功能我早就想實現了,但是苦於百度的文章大多是下載連結,一直沒捨得積分下載,這次通過查閱MSDN,將一套下載流程函式摸了一下,寫出程式碼實現從FTP下載檔案 環境 VS2015 程式碼: #include <iostream> #include <

c#程式碼實現排序演算法之歸併排序

歸併排序的平均時間複雜度為O(nlogn),最好時間複雜度為O(nlogn),最壞時間複雜度為O(nlogn),空間複雜度為O(n),是一種穩定的演算法。 1.將待排序序列r(1),r(2),…,r(n)劃分為兩個長度相等的子序列r(1),…r(n/2)和r(n/2+1),…,r

c#程式碼實現排序演算法之快速排序

快速排序的平均時間複雜度為O(nlog2n),最好時間複雜度為O(nlog2n),最壞時間複雜度為O(n²),空間複雜度為O(log2n),是一種不穩定的演算法。 1.劃分:選定一個記錄作為軸值,以軸值為基準將整個序列劃分為兩個子序列r(1)…r(i-1)和r(i+1)…r(n)

c#程式碼實現排序演算法之氣泡排序

氣泡排序的平均時間複雜度為O(n²),最好時間複雜度為O(n),最壞時間複雜度為O(n²),空間複雜度為O(1),是一種穩定的演算法。 1.將整個待排序的記錄序列劃分成有序區和無序區,初始時有序區為空,無序區包括所有待排序的記錄。 2.對無序區從前向後依次比較相鄰記錄,若反序則交

c#程式碼實現排序演算法之選擇排序

選擇排序的平均時間複雜度為O(n²),最好時間複雜度為O(n²),最壞時間複雜度為O(n²),空間複雜度為O(1),是一種不穩定的演算法。 1.將整個記錄序列劃分為有序區和無序區,初始時有序區為空,無序區含有待排序的所有記錄。 2.在無序區查詢值最小的記錄,將它與無序區的第一個記

c#程式碼實現排序演算法之插入排序

插入排序的平均時間複雜度為O(n²),最好時間複雜度為O(n),最壞時間複雜度為O(n²),空間複雜度為O(1),是一種穩定的演算法。 1.將整個待排序的記錄序列劃分成有序區和無序區,初始時有序區為待排序記錄序列的第一個記錄,無序區包括所有剩餘待排序的記錄。 2.將無序區的第一個

《大話資料結構4》—— 佇列的順序儲存結構 (迴圈佇列)—— C++程式碼實現

   佇列  ● 佇列的概念:   佇列(簡稱作隊,Queue)也是一種特殊的線性表,佇列的資料元素以及資料元素間的邏輯關係和線性表完全相同,其差別是線性表允許在任意位置插入和刪除,而佇列只允許在其一端進行插入操作在其另一端進行刪除操作。 佇

《大話資料結構5》—— 佇列的鏈式儲存結構 —— C++程式碼實現

目錄 鏈佇列 迴圈佇列和鏈式佇列的比較 鏈佇列 ●  實現佇列的最好的方式就是使用單鏈表來實現,佇列的鏈式儲存結構,其實就是線性表的單鏈表,只不過它只能尾進頭出而已——稱為鏈佇列。 ● 那為了操作方便,頭指標指向頭結點,隊尾指標指向終端節點,即最後一個結點元

Vivado HLS實現FIR濾波器(1)——使用官方C程式碼實現FIR

使用官方C程式碼實現FIR 參考資料: 不同點說明 程式結構 C語言程式碼 模擬綜合 參考資料: 教程——基於HLS實現FIR濾波器https://blog.csdn.net/LZY27

資料結構經典例題解析C/C++程式碼實現(二)

第一題 題目 編一C程式,它能把讀入的整數依次插入到一個初始為空的二叉排序樹中,一直讀到-9999為止(-9999不插入該二叉排序樹)。輸出該二叉排序樹的前序序列、後序序列及葉結點的個數。(輸入時,兩個相鄰的整數用空格隔開)。 解析 這個程式碼可以參考二叉樹

資料結構經典例題解析C/C++程式碼實現(一)

考研需要吧,雖然挺基礎的,但是還是要練習下的,而且,還可以幫助一些其他同樣需要這些程式碼的朋友。 實現最基礎的資料結構建議是用C語言,這樣子很多細節都可以很好地把握,當然,如果用STL可以簡單地實現,那麼我也會實現一下。 第一題 題目 編一C程式,它能根據讀入的資

《大話資料結構9》—— “二叉樹的順序儲存結構”——C++程式碼實現

  順序儲存結構: 二叉樹的順序儲存結構就是用一維陣列儲存二叉樹中的結點,並且結點的儲存位置,也就是陣列的下標要能體現結點之間的關秀,比如雙親與孩子的關係,左右結點的兄弟關係。 完全二叉樹: 完全二叉樹由於其結構上的特點,通常採用順序儲存方式儲存。一棵有n個結點的完全二

【初探】“ 選擇排序 ” ——C++程式碼實現

  選擇排序(Selection sort)是一種簡單直觀的排序演算法。無論什麼資料進去都是 O(n²) 的時間複雜度。所以用到它的時候,資料規模越小越好。唯一的好處可能就是不佔用額外的記憶體空間了吧。   簡單排序處理流程 首先在未排序序列中找到最小