HDU1532(最大流EK算法模板题)

Drainage Ditches

Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others)
Total Submission(s): 17484 Accepted Submission(s): 8238

Problem Description
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

Sample Output
50

Source
USACO 93

这是我做的第一道最大流的题,我这题是用EK算法写的,EK算法最关键的地方在于反向边的建立,这里也不好理解,为什么要建立反向边。首先我们假设没有反向边的情况,如有一条路径从源点到汇点,那么这条路径上我们取所有边的最小值,然后让这个最小值的流从源点流到汇点,这样一定是能够满足所有边的要求(因为我们找的是这条路径上最小的),这样我们就增加了从源点到汇点的流,但是现在我们要求一个最大流,所以我们希望有更多这样的路径,所以当我们找完所有的路径之后,得到的流就是最大流。但是,我们找每条路径时,每次都是随机找一条路径,所以会导致一个情况,就是找了这条路径之后,会对后面的路径有影响,导致最后得到的流的和并不是最大,在这里我画了一个图帮助理解。比如这个图,边的权值都为1
这里写图片描述
假设在没有反向边的情况下,我们找了这样一条路径(黑色标出),那么我们可以得到一个大小为1的流,但是找了这条路径之后就导致其它路径都不能找到流了,所以最后得到的最大流就是1,显然,这个图的最大流是2,1->2->4,1->3->4,每条路径为1,加起来为2.这样错误原因是因为我们选错了一条路径,在开始我们选择了1->2->3->4,这样就导致了我们得不到最大流,但是我们在上面说过,我们每次找路径都是随机找的,并不能保证每次能找到最“好”的路径。那么怎么办了?这时候反向边的作用就体现出来了。我们给每条边加一条反向边,权值和正向边相同(这个反向边在图里实际不存在),加了这些反向边之后就多了一些增广路径,弥补了前一条错误路径犯得错误,使得可以得到最大流,如下图
这里写图片描述
我只画出了2,4之间的反向边,这样虽然第一条路径1->2->3->4我们找“错了”,得到了一个大小为1的流,但是由于增加了反向边,所以我们还可以找到一条增广路径1->3->2->4,也得到了大小为1的流,然后两个大小为1的流加起来我们可以得到最大流2.看到这里你是不是会觉得这个反向边很神奇。能够弥补程序犯下的“错误”。

#include<bits/stdc++.h>
using namespace std;
const int maxn = 300;
const int inf = 1e9;
int N, M;
int Map[maxn][maxn];
int Maxflow;
int Flow[maxn];//流入节点i的流
bool visit[maxn];
int father[maxn];//用于路径回溯
void init()
{
    memset(Map, 0, sizeof(Map));
    Maxflow = 0;
}
void solve(int s, int e)
{

    queue<int> Q;
    while(1)//寻找增广路径,直到找不到增广路径为止推出循环
    {
        memset(visit, false, sizeof(visit));
        while(!Q.empty()) Q.pop();
        memset(Flow, 0, sizeof(Flow));
        Flow[s] = inf;//流入源点的流无穷大
        Q.push(s);
        while(!Q.empty())
        {
            int u = Q.front();
            Q.pop();
            visit[u] = true;
            for(int i = s; i <= e; i++)
            {
                if(!visit[i] && Map[u][i] > 0)
                {
                    visit[i] = true;
                    Q.push(i);
                    Flow[i] = min(Flow[u], Map[u][i]);//找出增广路径上的一个最小能满足所有的流
                    father[i] = u;//用于回溯
                }
            }
            if(Flow[e] > 0)//找到了汇点,准备反向回溯
            {
                for(int i = e; i != 1; i = father[i])
                {
                    Map[father[i]][i] -= Flow[e];
                    Map[i][father[i]] += Flow[e];
                }
                break;
            }

        }
        if(Flow[e] == 0) break;//找不到增广路径了,跳出循环
        Maxflow += Flow[e];
    }
}
int main()
{
    while(~scanf("%d%d", &N, &M))
    {
        init();
        int u, v, w;
        for(int i = 1; i <= N; i++)
        {
            scanf("%d%d%d", &u, &v, &w);
            Map[u][v] += w;
        }
        solve(1, M);
        printf("%d\n", Maxflow);

    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值