hdu6214 Smallest Minimum Cut

链接

http://acm.hdu.edu.cn/showproblem.php?pid=6214

题解

我这题就是直觉过题…其实我也不是很清楚为啥这么做…
就是把流量 × 2000 \times 2000 ×2000,最大流模 2000 2000 2000就是答案

感性理解一下,跑完最大流之后流量为 0 0 0的边组成了一个割
最小割肯定是这个割的子集
我把流量乘以 2000 2000 2000是先保证了最大流,因为每个边只有一个 1 1 1,所以如果,某一次增广选择的路径使得割边数目不必要地 + 1 +1 +1,答案却不会 + 1 +1 +1

比如下面的图:
在这里插入图片描述
显然我把最左边边权为 2 2 2的边割掉就是最小边数的最小割
而还有一种割法是割掉两条 1 1 1的边,显然是非最优解
在我跑最大流的时候,第一次跑了 1001 1001 1001的流,使得答案 + 1 +1 +1
第二次跑的流是 1000 1000 1000,答案并没有增加
这只是一种特殊情况,因为 2 = 1 + 1 2=1+1 2=1+1,所以边权为 2 2 2的边就成了两条边权为 1 1 1的边的替代品

但是如果是下面这种情况
在这里插入图片描述
这里两次增广都是跑了 1001 1001 1001的流量
其实第二次是边权为 3 3 3的边拿出了 2000 2000 2000中的一个 1 1 1来给后面用

考虑把每次增广的流建立有向无环图,把那些对应参量网络中剩余容量为 0 0 0的边打上标记
那么一个最小割就是一些打标记的边的集合,删掉这个集合中的边能使得 S S S到达不了 T T T
如果想让边数最小,就可以做一些优化,让第一张图中那样的两个 1 1 1换成一个 2 2 2

应该解释的还不是很清楚(因为我自己都不清楚)

代码

#include <bits/stdc++.h>
#define maxn 210
#define maxe 3000
#define iinf 0x3f3f3f3f
using namespace std;
struct Graph2
{
    int head[maxn], next[maxe], to[maxe], w[maxe], c[maxe], tot;
    void clear(int N)
    {
        int i;
        for(i=1;i<=N;i++)head[i]=0;
        for(i=2;i<=tot;i++)next[i]=0;
        tot=1;
    }
    void add(int a, int b, int ww, int cc){to[++tot]=b;next[tot]=head[a];w[tot]=ww;c[tot]=cc;head[a]=tot;}
    void adde(int a, int b, int ww,int cc){add(a,b,ww,cc);add(b,a,-ww,0);}
}G;
struct ISAP
{
    int S, T, last[maxn], d[maxn], num[maxn], Exit;
    void init(int N)
    {
        for(int i=1;i<=N;i++)d[i]=num[i]=0;
        num[0]=N;
        Exit=0;
    }
    int dfs(Graph2& G, int pos, int in)
    {
        int flow=0, t;
        if(pos==T)return in;
        for(int &p=last[pos];p;p=G.next[p])
            if(G.c[p] and d[G.to[p]]+1==d[pos])
            {
                flow+= t=dfs(G,G.to[p],min(in-flow,G.c[p]));
                G.c[p]-=t, G.c[p^1]+=t;
                if(Exit or in==flow)return flow;
            }
        Exit=--num[d[pos]]==0;
        ++num[++d[pos]];
        last[pos]=G.head[pos];
        return flow;
    }
    int maxflow(Graph2& G)
    {
        int ans(0);
        while(!Exit)ans+=dfs(G,S,iinf);
        return ans;
    }
}isap;
int read(int x=0)
{
    int c, f(1);
    for(c=getchar();!isdigit(c);c=getchar())if(c=='-')f=-f;
    for(;isdigit(c);c=getchar())x=x*10+c-0x30;
    return f*x;
}
int main()
{
    int T(read()), n, m, i;
    while(T--)
    {
        n=read(), m=read();
        G.clear(n);
        isap.init(n);
        isap.S=read(), isap.T=read();
        for(i=1;i<=m;i++)
        {
            auto u=read(), v=read(), w=read();
            G.adde(u,v,0,w*2000+1);
        }
        auto ans=isap.maxflow(G);
        cout<<ans%2000<<endl;
    }
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值