• 【ICPCBeijing 2006】狼抓兔子


    引理:最小割最大流定理

    搜了一圈没有找到什么是最小割,然后懵了。

    首先,什么是割?其实,割 \(=\) 割边 \(=\) 去掉以后使图不连通的边的集合。

    然后,容量和最少的割集称为最小割。对于割,有这样一个重要定理:

    最小割 \(=\) 最大流。

    嗯,最小割就这么多东西。为什么正确?这里给出一种直观的想法。(原 PO):

    1. 最大流不可能大于最小割,因为最大流所有的水流都一定经过最小割那些割边,流过的水流怎么可能比水管容量还大呢?
    2. 最大流不可能小于最小割,如果小,那么说明水管容量没有物尽其用,可以继续加大水流.

    证毕。

    思路

    这里首先说一个技巧,无向图的网络流在建边时反向弧直接建成权值为v的边即可,因为这样的边一开始就是可以增广的。

    题意是求一个无向图的最小割,然后我们运用最小割-最大流定理,可以转化成求最大流的问题。不过朴素的 Dinic 是会 \(\color{#052242}{\text{TLE}}\) 的,这里提供一种优化方法:

    我们知道,假定在一次 Dinic 过程中,发现不能再进行增广了,那么就相当于向下的这条路是废的。因此,我们可以直接把这条路堵上,然后就可以过了。

    代码

    #include<bits/stdc++.h>
    #define LL long long
    const LL N=8e6+5,inf=1e9;
    using namespace std;
    struct edge {
        LL to,nxt,w;
    } e[N];
    LL s,t,cnt=1,head[N],d[N];
    void add(LL u,LL v,LL w) {
        e[++cnt].to=v,e[cnt].w=w,e[cnt].nxt=head[u],head[u]=cnt;
    }
    bool bfs() {
        queue<LL> q;
        q.push(s),memset(d,0,sizeof(d)),d[s]=1;
        while(q.size()) {
            LL x=q.front();
            q.pop();
            for(LL i=head[x]; i; i=e[i].nxt) {
                LL v=e[i].to;
                if(e[i].w>0&&!d[v]) {
                    q.push(v),d[v]=d[x]+1;
                    if(v==t) {
                        return 1;
                    }
                }
            }
        }
        return 0;
    }
    LL dfs(LL x,LL sum) {
        if(x==t) {
            return sum;
        }
        LL res=0;
        for(int i=head[x]; i&&sum; i=e[i].nxt) {
            int v=e[i].to;
            if(!e[i].w||d[v]!=d[x]+1) {
                continue;
            }
            int k=dfs(v,min(sum,e[i].w));
            if(!k) {
                d[v]=-1;
            }
            e[i].w-=k,e[i^1].w+=k,res+=k,sum-=k;
        }
        return res;
    }
    LL n,m;
    int turn(int x,int y) {
        return m*(x-1)+y;
    }
    int main() {
        scanf("%lld %lld",&n,&m),s=1,t=turn(n,m);
        for(int i=1; i<=n; i++) {
            for(int j=1,w; j<m; j++) {
                scanf("%d",&w),add(turn(i,j),turn(i,j+1),w);
                add(turn(i,j+1),turn(i,j),w);
            }
        }
        for(int i=1; i<n; i++) {
            for(int j=1,w; j<=m; j++) {
                scanf("%d",&w),add(turn(i,j),turn(i+1,j),w);
                add(turn(i+1,j),turn(i,j),w);
            }
        }
        for(int i=1; i<n; i++) {
            for(int j=1,w; j<m; j++) {
                scanf("%d",&w),add(turn(i,j),turn(i+1,j+1),w);
                add(turn(i+1,j+1),turn(i,j),w);
            }
        }
        LL ans=0;
        while(bfs()) {
            ans+=dfs(s,inf);
        }
        printf("%lld",ans);
        return 0;
    }
    

    参考资料:网络流学习笔记(三) 最小割 平面图转对偶图 - LiRewriter 的博客 - 洛谷博客

    本文作者:AFewMoon,文章地址:https://www.cnblogs.com/AFewMoon/p/15527141.html

    知识共享许可协议

    本作品采用 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议 进行许可。

    限于本人水平,如果文章有表述不当之处,还请不吝赐教。

  • 相关阅读:
    [Java] 编写第一个java程序
    [Java] 环境变量设置
    [ActionScript 3.0] 常用的正则表达式
    [ActionScript 3.0] 正则表达式
    Python学习之==>URL编码解码&if __name__ == '__main__'
    Python学习之==>面向对象编程(一)
    Linux下安装redis-4.0.10
    Linux下编译安装Python-3.6.5
    Python学习之==>发送邮件
    Python学习之==>网络编程
  • 原文地址:https://www.cnblogs.com/AFewMoon/p/15527141.html
Copyright © 2020-2023  润新知