[USACO4.2]草地排水Drainage Ditches

传送门

这个题几乎可以用来作为最大流入门的题了,没有任何掩饰,直接告诉你这是最大流。
这里解释一下网络流的建反向边操作,这其实是一个退流操作,拿下面一个图举个例子:
1460350-20181206181744403-668904731.png
如果最先走了中间那条边,我们这个图就会变成这样:
1460350-20181206181957807-352622720.png
此时我们发现那条反向边能被另一条路径经过,但是这样不是错的吗,因为实际上这条路径是不存在的,让我们走走试试:
1460350-20181206182139948-299292541.png
诶,这不就等价于上面那条路走掉了50流量,下面那条路也走掉了50流量,让我们再仔细思考一下:中间那条边的状态又和最初一样了,就好像没有走过。
所以反向边走掉多少流量就等于退回正向边多少流量,然后我们就能发现这个做法显然是正确的。
自我认为没有什么别的难理解的地方了。
代码(dinic):

#include<cstdio>
#include<queue>
#include<cstring>
#define min(a,b) (a<b?a:b)
int dis[201],ans,n,m,cnt=1,s,t,inf=1e9+7,pre[401],nxt[401],h[201],v[401];std::queue<int>q;
void add(int x,int y,int z)
{
    pre[++cnt]=y,nxt[cnt]=h[x],h[x]=cnt,v[cnt]=z;
    pre[++cnt]=x,nxt[cnt]=h[y],h[y]=cnt;
}
bool bfs()
{
    memset(dis,0,sizeof dis);
    q.push(s),dis[s]=1;
    while(!q.empty())
    {
        int x=q.front();q.pop();
        for(int i=h[x];i;i=nxt[i])if(!dis[pre[i]]&&v[i])dis[pre[i]]=dis[x]+1,q.push(pre[i]);
    }
    return dis[t];
}
int dfs(int x,int flow)
{
    if(x==t||!flow)return flow;
    int f=flow;
    for(int i=h[x];i;i=nxt[i])
        if(v[i]&&dis[pre[i]]>dis[x])
        {
            int y=dfs(pre[i],min(v[i],f));
            f-=y,v[i]-=y,v[i^1]+=y;
            if(!f)return flow;
        }
    if(f==flow)dis[x]=-1;//这是一个优化,正确性显然 
    return flow-f;
}
int main()
{
    scanf("%d%d",&n,&m);s=1,t=m;
    for(int i=1,x,y,z;i<=n;i++)scanf("%d%d%d",&x,&y,&z),add(x,y,z);
    for(;bfs();ans+=dfs(s,inf));
    printf("%d\n",ans);
}

转载于:https://www.cnblogs.com/lcxer/p/10078384.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值