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;
}

猜你喜欢

转载自blog.csdn.net/Rainbow_storm/article/details/82916545
今日推荐