數據結構與算法分析 - 網絡流入門(Network Flow)

轉載:網絡流基礎篇——Edmond-Karp算法             BY納米黑客css

網絡流的相關定義:html

  • 源點:有n個點,有m條有向邊,有一個點很特殊,只出不進,叫作源點
  • 匯點:另外一個點也很特殊,只進不出,叫作匯點
  • 容量和流量:每條有向邊上有兩個量,容量和流量,從i到j的容量一般用c[i,j]表示,流量則一般是f[i,j].

一般能夠把這些邊想象成道路,流量就是這條道路的車流量,容量就是道路可承受的最大的車流量。很顯然的,流量<=容量。而對於每一個不是源點和匯點的點來講,能夠類比的想象成沒有存儲功能的貨物的中轉站,全部「進入」他們的流量和等於全部從他自己「出去」的流量。 算法

  • 最大流:把源點比做工廠的話,問題就是求從工廠最大能夠發出多少貨物,是不至於超過道路的容量限制,也就是,最大流

網絡流基礎篇——Edmond-Karp算法

求解思路:數組

首先,假如全部邊上的流量都沒有超過容量(不大於容量),那麼就把這一組流量,或者說,這個流,稱爲一個可行流網絡

一個最簡單的例子就是,零流,即全部的流量都是0的流。spa

  • (1).咱們就從這個零流開始考慮,假若有這麼一條路,這條路從源點開始一直一段一段的連到了匯點,而且,這條路上的每一段都知足流量<容量,注意,是嚴格的<,而不是<=。
  • (2).那麼,咱們必定能找到這條路上的每一段的(容量-流量)的值當中的最小值delta。咱們把這條路上每一段的流量都加上這個delta,必定能夠保證這個流依然是可行流,這是顯然的。
  • (3).這樣咱們就獲得了一個更大的流,他的流量是以前的流量+delta,而這條路就叫作增廣路。咱們不斷地從起點開始尋找增廣路,每次都對其進行增廣,直到源點和匯點不連通,也就是找不到增廣路爲止。
  • (4).當找不到增廣路的時候,當前的流量就是最大流,這個結論很是重要。

補充:code

  • (1).尋找增廣路的時候咱們能夠簡單的從源點開始作BFS,並不斷修改這條路上的delta 量,直到找到源點或者找不到增廣路。
  • (2).在程序實現的時候,咱們一般只是用一個c 數組來記錄容量,而不記錄流量,當流量+delta 的時候,咱們能夠經過容量-delta 來實現,以方便程序的實現。

 

相關問題:htm

爲何要增長反向邊?blog

在作增廣路時可能會阻塞後面的增廣路,或者說,作增廣路原本是有個順序才能找完最大流的。rem

但咱們是任意找的,爲了修正,就每次將流量加在了反向弧上,讓後面的流可以進行自我調整。

舉例:

好比說下面這個網絡流模型

3

咱們第一次找到了1-2-3-4這條增廣路,這條路上的delta值顯然是1。

因而咱們修改後獲得了下面這個流。(圖中的數字是容量)

4

這時候(1,2)和(3,4)邊上的流量都等於容量了,咱們再也找不到其餘的增廣路了,當前的流量是1。

可是,

這個答案明顯不是最大流,由於咱們能夠同時走1-2-4和1-3-4,這樣能夠獲得流量爲2的流。

那麼咱們剛剛的算法問題在哪裏呢

問題就在於咱們沒有給程序一個「後悔」的機會,應該有一個不走(2-3-4)而改走(2-4)的機制。

那麼如何解決這個問題呢

咱們利用一個叫作反向邊的概念來解決這個問題。即每條邊(i,j)都有一條反向邊(j,i),反向邊也一樣有它的容量。

咱們直接來看它是如何解決的:

在第一次找到增廣路以後,在把路上每一段的容量減小delta的同時,也把每一段上的反方向的容量增長delta。

           c[x,y]-=delta;
           c[y,x]+=delta;


咱們來看剛纔的例子,在找到1-2-3-4這條增廣路以後,把容量修改爲以下:

1

這時再找增廣路的時候,就會找到1-3-2-4這條可增廣量,即delta值爲1的可增廣路。將這條路增廣以後,獲得了最大流2。

2

那麼,這麼作爲何會是對的呢?

事實上,當咱們第二次的增廣路走3-2這條反向邊的時候,就至關於把2-3這條正向邊已是用了的流量給「退」了回去,不走2-3這條路,而改走從2點出發的其餘的路也就是2-4。

若是這裏沒有2-4怎麼辦?

這時假如沒有2-4這條路的話,最終這條增廣路也不會存在,由於他根本不能走到匯點

同時原本在3-4上的流量由1-3-4這條路來「接管」。而最終2-3這條路正向流量1,反向流量1,等於沒有流。

 

附錄:(edmonds-Karp版本)

 1: void update_residual_network(int u,int flow){                                             
 2:     while(pre[u]!=-1){
 3:         map[pre[u]][u]-=flow;                   
 4:         map[u][pre[u]]+=flow;                  
 5:         u=pre[u];
 6:     }
 7: }
 8: int find_path_bfs(int s,int t){
 9:     memset(visited,0,sizeof(visited)); 
 10:     memset(pre,-1,sizeof(pre));
 11:     visited[s]=1;
 12:     int min=INF;
 13:     queue<int> q;
 14:     q.push(s);
 15:  
 16:     while(!q.empty()){
 17:         int cur=q.front();q.pop();
 18:         if(cur==t)   break;  
 19:  
 20:         for(int  i = 1 ; i <= m ; i++ ){
 21:             if( visited[i] == 0 && map[cur][i] != 0){
 22:                 q.push(i);
 23:                 min=(min<map[cur][i]?min:map[cur][i]) ;
 24:                 pre[i]=cur;
 25:                 visited[i]=1;
 26:             }
 27:         }
 28:     }
 29:     if(pre[t]==-1) return 0;
 30:     
 31:     return min;
 32: }
 33: int edmonds_karp(int s,int t){
 34:     int new_flow=0;
 35:     int max_flow=0;
 36:     do{
 37:         new_flow = find_path_bfs(s,t);
 38:         update_residual_network(t,new_flow);
 39:         max_flow += new_flow;
 40:     }while( new_flow != 0 );
 41:     return max_flow;
 42: }
相關文章
相關標籤/搜索