最大流EK算法


給定一個有向圖G=(V,E),把圖中的邊看作
管道,每條邊上有一個權值,表示該管道
的流量上限。給定源點s和匯點t,現在假設
在s處有一個水源,t處有一個蓄水池,問從
s到t的最大水流量是多少?
1

網絡流圖里,源點流出的量,等於匯點流
入的量,除源匯外的任何點,其流入量之
和等於流出兩之和。
下面我們來考慮如何求最大流。
首先,假如所有邊上的流量都沒有超過容量(水管),那么就把這個流,稱為一個可行流。易見,任一網絡中都有一個零流,即每弧a上f(a)=0的流f.

我們就從這個零流開始考慮,假如有這么一條路,這條路從源點開始一直一段一段的連到了匯點(這條路叫做可行路徑),並且,這條路上的每一段都滿足流量<容量,注意,是嚴格的<,而不是<=。那么,我們一定能找到這條路上的每一段的(容量-流量)的值當中的最小值delta。我們把這條路上每一段的流量都加上這個delta,一定可以保證這個流依然是可行流,這是顯然的。
這樣我們就得到了一個更大的流,他的流量是之前的流量+delta,而這條路就叫做增廣路。我們不斷地從起點開始尋找增廣路,每次都對其進行增廣,直到源點和匯點不連通,也就是找不到增廣路為止。當找不到增廣路的時候,當前的流量就是最大流。但這個想法是否正確?
2

考慮上面這樣的圖,如果我們沿着s-a-b-t路線走僅能得到一個100的流,實際上此圖存在流量為200的流(sat+abt).問題出在過早地認為邊a → b上流量不為0,因而“封鎖”了流量繼續增大的可能。

一種解決方法是在第一次找到增廣路之后,在把路上每一段的容量減少delta的同時,也把每一段上的反方向的容量增加delta。即在Dec(c[x,y],delta)的同時,inc(c[y,x],delta)
第一次找到增廣路: 3
第一次找到增光路添加反向邊后得到的新圖:
4
這樣我們第二次搜索的時候就可以在新的網絡里找到新的路徑這是一個取消流的操作也可以看作是兩條路徑的合並。

5

第二次搜索又找到了一個流量為100的流,加上第一次搜索得到的流量為100的流,總流量上升到200

6

再對第二次搜索后的圖添加反向邊,變成:
7
在此圖上再次進行dfs,已經找不到路徑了,所以流量無法再增加,最大流就是200

在找增廣路的時候采用廣度優先的思想,我們就叫它EdmondsKarp算法。他是Ford&Fulkerson算法的改進。下面以一個題目為例給出代碼:
poj1273
題目大意:
現在有m個池塘(從1到m開始編號,1為源點,m為匯點),及n條水渠,給出這n條水渠所連接的池塘和所能流過的水量,求水渠中所能流過的水的最大容量.
輸入:
5 4
1 2 40
1 4 20
2 4 20
2 3 30
3 4 10
輸出:
50

#include<bits/stdc++.h>
using namespace std;
const int maxn=205;
const int inf=0x7fffffff;
int r[maxn][maxn]; //殘留網絡,初始化為原圖
bool visit[maxn];
int pre[maxn];
int m,n;
bool bfs(int s,int t)  //尋找一條從s到t的增廣路,若找到返回true
{
    int p;
    queue<int > q;
    memset(pre,-1,sizeof(pre));
    memset(visit,false,sizeof(visit));
    pre[s]=s;
    visit[s]=true;
    q.push(s);
    while(!q.empty())
    {
        p=q.front();
        q.pop();
        for(int i=1;i<=n;i++)
        {
            if(r[p][i]>0&&!visit[i])
            {
                pre[i]=p;
                visit[i]=true;
                if(i==t) return true;
                q.push(i);
            }
        }
    }
    return false;
}
int EdmondsKarp(int s,int t)
{
   int flow=0,d,i;
   while(bfs(s,t))
   {
       d=inf;
       for(i=t;i!=s;i=pre[i])
           d=d<r[pre[i]][i]? d:r[pre[i]][i];
       for(i=t;i!=s;i=pre[i])
       {
           r[pre[i]][i]-=d;
           r[i][pre[i]]+=d;
       }
       flow+=d;
   }
   return flow;
}

int main()
{
    while(scanf("%d%d",&m,&n)!=EOF)
    {
        int u,v,w;
        memset(r,0,sizeof(r));///
        for(int i=0;i<m;i++)
        {
            scanf("%d%d%d",&u,&v,&w);
            r[u][v]+=w;
        }
        printf("%d\n",EdmondsKarp(1,n));
    }
    return 0;
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM