1. 程式人生 > >Drainage Ditches(網路最大流)

Drainage Ditches(網路最大流)

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

題意:圖論書P270:Joho在農場修建了一套排水系統,並且在每條排水溝的起點安裝了調節閥門。這樣他就可以控制流入排水溝的水流速度了。已知每條排水後排水溝的方向u->v,以及每條排水溝的最大速度c。求此排水溝的最大流水速度,起點(1)為農場,終點(n)為池塘。

思路:網路流的入門題。我們用結構體儲存每一條邊的容量c以及最大流量,通過bfs我們找到一條從起點到終點的鏈,然後在鏈上找到最大增改值,然後修改結構體的f,重複操作。。。Orz。。。程式碼上有詳細的註釋!

程式碼如下:

#include<cstdio>
#include<cstring>
#include<queue>
#define MAXN 210
#define inf 0x3f3f3f3f
using namespace std;
struct node  //G(u,v)
{
    int c,f;
} Edge[MAXN][MAXN];

int M,N; 
int s,t;//起點,終點

int residual[MAXN][MAXN];//殘留網路
int pre[MAXN];  //鏈
int vis[MAXN];  //標記

int maxflow,min_augment; //最大流,可增進量

void find_augment_path()
{
    memset(vis,0,sizeof(vis));  //標記清空
    memset(pre,0,sizeof(pre));  //鏈清空
    memset(residual,0,sizeof(residual)); //殘留容量清空
    queue<int>Q;  
    Q.push(s);  //鏈的第一個為起點 s
    vis[s]=1;   //標記s
    while(!Q.empty()&&!pre[t])  //佇列不為空,並且沒有到達終點 t
    {
        int cu=Q.front();  //取隊首,
        Q.pop();
        for(int i=1; i<=N; i++)  //遍歷所有的點
        {
            if(!vis[i])  //如果這個點沒有被用過
            {
                if(Edge[cu][i].c - Edge[cu][i].f > 0)  //前向弧 c(u,v) > f(u,v)
                {
                    residual[cu][i] = Edge[cu][i].c - Edge[cu][i].f;//殘留網路
                    pre[i]=cu,vis[i]=1,Q.push(i); //擴充套件鏈,入佇列,標記
                }
                else if(Edge[i][cu].f > 0)  //後向弧  f(u,v) > 0
                {
                    residual[cu][i] = Edge[i][cu].f; //雖然是後向弧,但殘留網路要按前向弧計算
                    pre[i]=cu,vis[i]=1,Q.push(i);  //擴充套件鏈,入佇列,標記
                }
            }
        }
    }
}

void augment_flow() //尋找可增改量,即鏈中的最小值
{
    int i=t,j=inf;//i為終點,從終點開始找
    if(!pre[i])  //如果擴充套件的條鏈沒有找到終點
    {
        min_augment = 0; //那麼可增改量就是 0
        return;
    }
    while(i!=s) //因為是從起點 s 開始找的,所有 s 為查詢終點 
    {
        /*
            因為bfs的時候是倒序儲存的(pre[i] = cu ...)
            即 residual(u,v)   此時 v = i,  u = pre[i];
        */
        if(residual[pre[i]][i] < j)  //找這條鏈中的最小值
            j=residual[pre[i]][i];
        i=pre[i];  //迭代維護 i
    }
    min_augment = j; //可增改量就是 j
}

void update_flow() //更新 f(u,v)
{
    int i=t;
    if(!pre[i])return;//終點沒有被找到
    
    while(i!=s)//和上述 augment_flow() 查詢方式一樣
    {
        if(Edge[pre[i]][i].c-Edge[pre[i]][i].f > 0) //前向弧
            Edge[pre[i]][i].f+=min_augment;  //f(u,v) += min_augment
        else if(Edge[i][pre[i]].f > 0)              //後向弧
            Edge[pre[i]][i].f+=min_augment;  //f(u,v) += min_augment
        i=pre[i];  //迭代維護
    }
}

void solve()
{
    s=1,t=N;//起點,終點
    maxflow = 0; //最大流初始化為 0
    while(1)  //一直迴圈
    {
        find_augment_path();//先用bfs找增廣路
        augment_flow();     //在鏈中找 可增進量
        maxflow+=min_augment;  //最大流 + 改增進量
        if(min_augment > 0)  //如果存在增進量,就要更新 f(u,v) 了
            update_flow();
        else                 //否則的話,說明已經不存在增廣路了,直接退出即可
            break;
    }
}
int main()
{
    int u,v,c;
    while(~scanf("%d%d",&M,&N))
    {
        memset(Edge,0,sizeof(Edge));
        for(int i=0; i<M; i++)
        {
            scanf("%d%d%d",&u,&v,&c);  //起點,終點,容量
            Edge[u][v].c+=c;     //累計容量
        }
        solve();
        printf("%d\n",maxflow);//輸出最大流
    }
}