hdu 3549 Flow Problem (最大流 EK)

Flow Problem

Time Limit: 5000/5000 MS (Java/Others)    Memory Limit: 65535/32768 K (Java/Others)
Total Submission(s): 22504    Accepted Submission(s): 10516


 

Problem Description

Network flow is a well-known difficult problem for ACMers. Given a graph, your task is to find out the maximum flow for the weighted directed graph.

 

 

Input

The first line of input contains an integer T, denoting the number of test cases.
For each test case, the first line contains two integers N and M, denoting the number of vertexes and edges in the graph. (2 <= N <= 15, 0 <= M <= 1000)
Next M lines, each line contains three integers X, Y and C, there is an edge from X to Y and the capacity of it is C. (1 <= X, Y <= N, 1 <= C <= 1000)

 

 

Output

For each test cases, you should output the maximum flow from source 1 to sink N.

 

 

Sample Input


 

2 3 2 1 2 1 2 3 1 3 3 1 2 1 2 3 1 1 3 1

 

 

Sample Output


 

Case 1: 1 Case 2: 2

 

 

Author

HyperHexagon

 

 

Source

HyperHexagon's Summer Gift (Original tasks)

 

 

題意:網絡流問題,用一個有向圖來表示網絡結構,有n個點,m條邊,這些邊上有一個權值表示的是這條邊最多能夠通過的流量,現在 在1點傳輸流量,求n點最多能夠得到的流量。

思路:假設經過邊e的流量爲f(e),邊e的容量爲c(e),流量始終是守恆的,從源點出發的流量=等於終點收到的流量。(好比你要灌溉農田,你從水源點引水到田裏,源點出發的所有水都會到達目標點,既不會增加也不會減少)。流經邊e的流量:f(e)=min(flow,c(e))(總不能比你的容量還大吧)。我們畫出一個網絡流圖。

我們常規思路考慮,首先找到第一條能夠從源點到達目標點的路徑(這樣的路徑我們稱爲增廣路,因爲最初圖的流量是0,現在增加了圖中的流量),然後使得這條路徑流經的流量儘可能的大。然後又找第二條增廣路,然後找第三條這增廣路,然後找第四條增廣路。。。直到圖中沒有增廣路了。

從圖中得知我們能夠沿着1->2->3->5的路徑流過5的流量

然後沿着1->2->4->5的路徑流過5的流量

這時圖中找不出增廣路了,那麼我們圖中一共有10的流量,答案就應該是10嗎?

正確答案不是10,而是11。

從1->2->4->5流經6流量,從1->2->3->5流經5流量,從1->3->5流經1流量。總共答案是11.

爲什麼會出現這樣的情況呢?原因是我們分配的不合理,前面的流量分配固定了,後面的流量分配收到限制。解決的辦法是,給每一條邊都增加一條反向邊,程序走反向邊,意思是這條路的流量從新分配,相當於給了程序一個反悔的機會,以便最大化的分配流量。

求最大流的方法有很多種如Dinic,Edmonds-Karp、Ford-Fulkerson以及Shortest Augmenting Paths.

我使用的是Edmonds-Karp,它的效率不高適合數據較小。算法的核心就是通過BFS不斷尋找圖中的增廣路進行增廣。因爲有反向邊,所以能夠使圖中的流量最大化。

AC代碼:

#include <queue>
#include <cstdio>
#include <cstring>
using namespace std;
const int INF = 0x3f3f3f3f;
const int MAXN = 20;
const int MAXM = 1005;
struct Edge{
    int next,v,w;
}edge[MAXM<<1];

int cnt;
int head[MAXN],pre[MAXN],rec[MAXN],flow[MAXN];
queue<int> q;

void add(int u,int v,int w){
    edge[cnt]=(Edge){head[u],v,w};
    head[u]=cnt++;
    //反向邊
    edge[cnt]=(Edge){head[v],u,0};
    head[v]=cnt++;
}

int bfs(int s,int t){
    memset(pre,-1,sizeof(pre));
    while(!q.empty())
        q.pop();
    pre[s]=s;flow[s]=INF;
    q.push(s);
    while(!q.empty()){
        int top = q.front();q.pop();
        for(int i=head[top];~i;i=edge[i].next){
            int v=edge[i].v;
            if(pre[v]==-1&&edge[i].w>0){
                //由top點流向v點的流量=來自於top點的流量和這條管道的容量取一個較小的值。
                flow[v]=min(flow[top],edge[i].w);
                //記錄增廣路
                pre[v]=top;
                rec[v]=i;
                q.push(v);
            }
        }
        if(pre[t]!=-1) return flow[t];
    }
    return -1;
}

//源點與目標點
int EK(int s,int t){
    //tmp是不斷增加的流
    int ans=0,tmp;
    //不斷的增加流量直到找不到增廣路
    while((tmp=bfs(s,t))!=-1){
        ans+=tmp;
        int k=t;
        while(k!=s){
            edge[rec[k]].w-=tmp;//正向邊
            edge[rec[k]^1].w+=tmp;//反向邊
            k=pre[k];
        }
    }
    return ans;
}

int main(){
    int n,m,T,cae=0;
    scanf("%d",&T);
    while(T--){
        scanf("%d%d",&n,&m);
        cnt=0;
        memset(head,-1,sizeof(head));
        for(int i=0;i<m;i++){
            int u,v,w;
            scanf("%d%d%d",&u,&v,&w);
            add(u,v,w);
        }
        printf("Case %d: %d\n",++cae,EK(1,n));
    }
    return 0;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章