網絡最大流算法之Ford_Fullkerson方法,EK算法c++模板

文章參考:http://blog.csdn.net/pi9nc/article/details/23339111

該算法最精華的部分是反向邊的理解,

即修改容量的時候爲什麼反向邊加上該值,

    c[pre[i]][i]-=_min;
    c[i][pre[i]]+=_min;
            

參考:http://blog.sina.com.cn/s/blog_6cf509db0100uy5n.html



在算法導論中對求解最大流問題給出了一般性的解決方法,但並沒有涉及到具體的實現。在這裏我還是重新的對求解最大流的思想進行一般性的描述,然後再給出具體的實現。

      Ford-Fulkerson方法依賴於三種重要思想,這三個思想就是在上一篇網絡流基礎中提到的:殘留網絡,增廣路徑和割。Ford-Fulkerson方法是一種迭代的方法。開始時,對所有的u,v∈V有f(u,v)=0,即初始狀態時流的值爲0。在每次迭代中,可通過尋找一條“增廣路徑”來增加流值。增廣路徑可以看成是從源點s到匯點t之間的一條路徑,沿該路徑可以壓入更多的流,從而增加流的值。反覆進行這一過程,直至增廣路徑都被找出來,根據最大流最小割定理,當不包含增廣路徑時,f是G中的一個最大流。在算法導論中給出的Ford-Fulkerson實現代碼如下:

     FORD_FULKERSON(G,s,t)
     1   for each edge(u,v)∈E[G]
     2        do f[u,v] <— 0
     3            f[v,u] <— 0
     4   while there exists a path p from s to t in the residual network Gf
     5        do cf(p) <— min{ cf(u,v) : (u,v) is in p }
     6        for each edge(u,v) in p
     7             do f[u,v] <— f[u,v]+cf(p)         //對於在增廣路徑上的正向的邊,加上增加的流
     8                  f[v,u] <— -f[u,v]                //對於反向的邊,根據反對稱性求

     第1~3行初始化各條邊的流爲0,第4~8就是不斷在殘留網絡Gf中尋找增廣路徑,並沿着增廣路徑的方向更新流的值,直到找不到增廣路徑爲止。而最後的最大流也就是每次增加的流值cf(p)之和。在實際的實現過程中,我們可以對上述代碼做些調整來達到更好的效果。如果我們採用上面的方法,我們就要保存兩個數組,一個是每條邊的容量數組c,一個就是上面的每條邊的流值數組f,在增廣路徑中判斷頂點u到v是否相同時我們必須判斷c[u][v]-f[u][v]是否大於0,但是因爲在尋找增廣路徑時是對殘留網絡進行查找,所以我們可以只保存一個數組c來表示殘留網絡的每條邊的容量就可以了,這樣我們在2~3行的初始化時,初始化每條邊的殘留網絡的容量爲G的每條邊的容量(因爲每條邊的初始流值爲0)。而更新時,改變7~8行的操作,對於在殘留網絡上的邊(u,v)執行c[u][v]-=cf(p),而對其反向的邊(v,u)執行c[v][u]+=cf(p)即可。

      現在剩下的最關鍵問題就是如何尋找增廣路徑。而Ford-Fulkerson方法的運行時間也取決於如何確定第4行中的增廣路徑。如果選擇的方法不好,就有可能每次增加的流非常少,而算法運行時間非常長,甚至無法終止。對增廣路徑的尋找方法的不同形成了求最大流的不同算法,這也是Ford-Fulkerson被稱之爲“方法”而不是“算法”的原因。下面將給出Ford-Fulkerson方法的具體實現細節:

int c[MAX][MAX];  //殘留網絡容量
int pre[MAX];  //保存增廣路徑上的點的前驅頂點
bool visit[MAX];

int Ford_Fulkerson(int src,int des,int n){   //src:源點 des:匯點 n:頂點個數
     int i,_min,total=0;
     while(true){
         if(!Augmenting_Path(src,des,n))return total; //如果找不到增廣路就返回,在具體實現時替換函數名
         _min=(1<<30);
         i=des;
         while(i!=src){   //通過pre數組查找增廣路徑上的邊,求出殘留容量的最小值
             if(_min>c[pre[i]][i])_min=c[pre[i]][i];
             i=pre[i];
         }
         i=des;
         while(i!=src){    //再次遍歷,更新增廣路徑上邊的流值
             c[pre[i]][i]-=_min;
             c[i][pre[i]]+=_min;
             i=pre[i];
         }
         total+=_min;     //每次加上更新的值
     }
}


Edmonds-Karp算法實際上就是採用廣度優先搜索來實現對增廣路徑的p的計算,代碼如下:

bool Edmonds_Karp(int src,int des,int n){
     int v,i;
     for(i=0;i<n;i++)visit[i]=false;
     front=rear=0;     //初始化
     que[rear++]=src;
     visit[src]=true;
     while(front!=rear){     //將源點進隊後開始廣搜的操作
         v=que[front++]; 
         
//這裏如果採用鄰接表的鏈表實現會有更好的效率,但是要注意(u,v)或(v,u)有任何一條
         //邊存在於原網絡流中,那麼鄰接表中既要包含(u,v)也要包含(v,u)
         for(i=0;i<n;i++){ 
             if(!visit[i]&&c[v][i]){  
//只有殘留容量大於0時才存在邊
                 que[rear++]=i;
                 visit[i]=true;
                 pre[i]=v;
                 if(i==des)return true;   //如果已經到達匯點,說明存在增廣路徑返回true
             }
         }
     }
     return false;
}



完整版代碼,我覺得寫得很清楚,可以用鄰接表優化:

  1. #include<iostream>  
  2.  #include<queue>  
  3.  using namespace std;  
  4.  const int maxn=205;  
  5.  const int inf=0x7fffffff;  
  6.   
  7. int r[maxn][maxn]; //殘留網絡,初始化爲原圖  
  8. bool visit[maxn];  
  9.  int pre[maxn];  
  10.  int m,n;  
  11.   
  12. bool bfs(int s,int t)  //尋找一條從s到t的增廣路,若找到返回true  
  13.  {  
  14.      int p;  
  15.      queue<int > q;  
  16.      memset(pre,-1,sizeof(pre));  
  17.      memset(visit,false,sizeof(visit));  
  18.   
  19.     pre[s]=s;  
  20.      visit[s]=true;  
  21.      q.push(s);  
  22.      while(!q.empty())  
  23.      {  
  24.          p=q.front();  
  25.          q.pop();  
  26.          for(int i=1;i<=n;i++)  
  27.          {  
  28.              if(r[p][i]>0&&!visit[i])  
  29.              {  
  30.                  pre[i]=p;  
  31.                  visit[i]=true;  
  32.                  if(i==t) return true;  
  33.                  q.push(i);  
  34.              }  
  35.          }  
  36.      }  
  37.      return false;  
  38.  }  
  39.   
  40. int EdmondsKarp(int s,int t)  
  41.  {  
  42.     int flow=0,d,i;  
  43.     while(bfs(s,t))  
  44.     {  
  45.         d=inf;  
  46.         for(i=t;i!=s;i=pre[i])  
  47.             d=d<r[pre[i]][i]? d:r[pre[i]][i];  
  48.         for(i=t;i!=s;i=pre[i])  
  49.         {  
  50.             r[pre[i]][i]-=d;  
  51.             r[i][pre[i]]+=d;  
  52.         }  
  53.         flow+=d;  
  54.     }  
  55.     return flow;  
  56.  }  
  57.   
  58.   
  59.  int main()  
  60.  {  
  61.      while(scanf("%d%d",&m,&n)!=EOF)  
  62.      {  
  63.          int u,v,w;  
  64.          memset(r,0,sizeof(r));///  
  65.          for(int i=0;i<m;i++)  
  66.          {  
  67.              scanf("%d%d%d",&u,&v,&w);  
  68.              r[u][v]+=w;  
  69.          }  
  70.          printf("%d\n",EdmondsKarp(1,n));  
  71.      }  
  72.      return 0;  
  73.  }  
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章