1. 程式人生 > >網路流問題——最大流 POJ 1273

網路流問題——最大流 POJ 1273

例題POJ 1273

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

網路流問題(來自北京大學ACM暑期課講義–郭煒)

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


基本思路很簡單,每次用dfs從源到匯找一條可行路徑, 然後把這條路塞滿。這條路徑上容量最小的那條邊的容量,就是這次dfs所找到的流量。然後對於路徑上的每條邊,其容量要減去剛才找到的流量。對上次dfs時找到的流量路徑上的邊,新增一條“反向”邊,反向邊上的容量等於上次dfs時找到的該邊上的流量,然後再利用“反向”的容量和其他邊上剩餘的容量尋找路徑。當再次進行dfs,已經找不到路徑了,所以流量無法再增加,此即為最大流。

殘餘網路

在一個網路流圖上,找到一條源到匯的路徑(即找到了一個流量)後,對路徑上所有的邊,其容量都減去此次找到的流量,對路徑上所有的邊,都新增一條反向邊,其容量也等於此次找到的流量,這樣得到的新圖,就稱為原圖的“殘餘網路”

Edmonds-Karp演算法

求最大流的過程,就是不斷找到一條源到匯的路徑,然後構建殘餘網路,再在殘餘網路上尋找新的路徑,使總流量增加,然後形成新的殘餘網路,再尋找新路徑…..直到某個殘餘網路上找不到從源到匯的路徑為止,最大流就算出來了。
每次尋找新流量並構造新殘餘網路的過程,就叫做尋找流量的“增廣路徑”,也叫“增廣”。在每次增廣的時候,選擇從源到匯的具有最少邊數的增廣路徑,即不是通過dfs尋找增廣路徑,而是通過bfs尋找增廣路徑。已經證明這種演算法的複雜度上限為nm2 (n是點數,m是邊數)。

分析

此處採用領接矩陣來儲存相關資料。
赤裸裸的網路流題目。
解題過程和上面分析的過程一摸一樣。

樣例實現程式碼

/*
 [email protected]
*/
#include<iostream>
#include<algorithm>
#include<cstring>
#include<queue>
#define maxn 205 
using namespace std;
long long G[maxn][maxn];
bool vis[maxn];
int pre[maxn];
int n,m;
long long Karp(){
    int v;
    queue<int>q;
    bool findp = false;
    memset(pre,0,sizeof(pre));
    memset(vis,false,sizeof(vis));
    vis[1]=true;
    pre[1]=0;
    q.push(1);
    while(!q.empty()){
        v=q.front();
        q.pop();
        for(int i=1;i<=m;++i){
            if(vis[i]==0&&G[v][i]>0){
                pre[i]=v;
                vis[i]=true;
                if(i==m){
                    findp=true;
                    while(!q.empty())
                        q.pop();
                    break;
                }
                else
                    q.push(i);
            }
        }
    }
    if(findp=false)
        return 0;
    long long minf = G[pre[m]][m];
    v=pre[m];
    while(pre[v]){
        minf = min(minf,G[pre[v]][v]);
        v=pre[v];
    }
    v=m;
    while(pre[v]){
        G[pre[v]][v] -= minf;
        G[v][pre[v]] += minf;
        v = pre[v];
    }
    return minf;
}
int main(){
    int from,to,cap;
    long long maxf,aug;
    while(cin>>n>>m){
        maxf = 0;
        memset(G,0,sizeof(G));
        for(int i=0;i<n;++i){
            cin>>from>>to>>cap;
            G[from][to]+=cap;
        }
        while(aug=Karp()){
            maxf += aug;
        }
        cout<<maxf<<endl;
    }
    return 0;
}

結果

這裡寫圖片描述