POJ1273 Drainage Ditches(网络流--最大流,EK增广路算法)

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
3 4 10

Sample Output

50

思路

这是一道最大流的模板题,给你m条边和n个点,然后给出每个边的两点以及权值,求最大流。

关于网络流的定义不多说,很多资料都有。说一下具体的思想,思想就是:

首先我们要有三个概念:

  • 实流网络:只显示每条边的实际流量,不显示容量
  • 残余网络:在残余网络中,与网络边对应的同向边是可增量(还可以增加多少流量),反向边是实际流量
  • 可增广路:在残余网络中,一条从源点到汇点的简单路径

福特-福克森算法的思想是对于一个残余网络,不断地寻找可增广路,然后把每次的可增广量累加起来,就是最后的最大流

  • 增广路定理:设flow是网络G的一个可行流,如果不存在从源点s到汇点t关于flow的可增广路p,则flowG的一个最大流。

那么EK算法解决的问题自然就是寻找增广路的过程了,如果寻找增广路的过程用的算法不对的话,那么时间复杂度就会很高,实质上,EK算法就是利用广度优先搜索来遍历一个图,然后用一个数组pre来记录一个点的前驱,然后寻找增广路,在实流网络增流,在残余网络减流,最后累加的可增广量就是答案。

代码

#include <cstdio>
#include <cstring>
#include <cctype>
#include <stdlib.h>
#include <string>
#include <map>
#include <iostream>
#include <stack>
#include <cmath>
#include <queue>
#include <vector>
#include <algorithm>
using namespace std;
typedef long long ll;
#define inf 0x3f3f3f3f
#define mem(a,b) memset(a,b,sizeof(a))
const int N=200+20;
int g[N][N],f[N][N],pre[N];//分别代表残余网络,实流网络,前驱数组
bool vis[N];//标记数组
int n,m;//点数和边数
bool bfs(int s,int t)
{
    mem(pre,-1);
    mem(vis,false);
    queue<int>q;
    vis[s]=true;
    q.push(s);
    while(!q.empty())
    {
        int now=q.front();
        q.pop();
        for(int i=1; i<=n; i++)
        {
            if(!vis[i]&&g[now][i]>0)
            {
                vis[i]=true;
                pre[i]=now;
                if(i==t)
                    return true;
                q.push(i);
            }
        }
    }
    return false;
}
int EK(int s,int t)
{
    int v,w,d,maxflow=0;
    while(bfs(s,t))
    {
        v=t;
        d=inf;
        while(v!=s)
        {
            w=pre[v];
            d=min(d,g[w][v]);
            v=w;
        }
        maxflow+=d;
        v=t;
        while(v!=s)
        {
            w=pre[v];
            g[w][v]-=d;
            g[v][w]+=d;
            if(f[v][w]>0)
                f[v][w]-=d;
            else
                f[w][v]+=d;
            v=w;
        }
    }
    return maxflow;
}
int main()
{
    int a,b,c;
    while(~scanf("%d%d",&m,&n))
    {
        mem(g,0);
        mem(f,0);
        for(int i=1; i<=m; i++)
        {
            scanf("%d%d%d",&a,&b,&c);
            g[a][b]+=c;
        }
        printf("%d\n",EK(1,n));
    }
    return 0;
}

取消实流网络f以后:

#include <cstdio>
#include <cstring>
#include <cctype>
#include <stdlib.h>
#include <string>
#include <map>
#include <iostream>
#include <stack>
#include <cmath>
#include <queue>
#include <vector>
#include <algorithm>
using namespace std;
typedef long long ll;
#define inf 0x3f3f3f3f
#define mem(a,b) memset(a,b,sizeof(a))
const int N=200+20;
int g[N][N],pre[N];//分别代表残余网络,前驱数组
bool vis[N];//标记数组
int n,m;//点数和边数
bool bfs(int s,int t)
{
    mem(pre,-1);
    mem(vis,false);
    queue<int>q;
    vis[s]=true;
    q.push(s);
    while(!q.empty())
    {
        int now=q.front();
        q.pop();
        for(int i=1; i<=n; i++)
        {
            if(!vis[i]&&g[now][i]>0)
            {
                vis[i]=true;
                pre[i]=now;
                if(i==t)
                    return true;
                q.push(i);
            }
        }
    }
    return false;
}
int EK(int s,int t)
{
    int v,w,d,maxflow=0;
    while(bfs(s,t))
    {
        v=t;
        d=inf;
        while(v!=s)
        {
            w=pre[v];
            d=min(d,g[w][v]);
            v=w;
        }
        maxflow+=d;
        v=t;
        while(v!=s)
        {
            w=pre[v];
            g[w][v]-=d;
            g[v][w]+=d;
            v=w;
        }
    }
    return maxflow;
}
int main()
{
    int a,b,c;
    while(~scanf("%d%d",&m,&n))
    {
        mem(g,0);
        for(int i=1; i<=m; i++)
        {
            scanf("%d%d%d",&a,&b,&c);
            g[a][b]+=c;
        }
        printf("%d\n",EK(1,n));
    }
    return 0;
}
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值