最小割3(二分图)


最小割3(二分图)


最小权覆盖集

概述

选取任意的点,使得点集可以包含所有的边(每一条边的顶点至少有一个被选择)。

证明

首先进行图的二分然后从S到左边图连一个容量为点权的边,右边图连一个向T容量也为点权的边,中间的点都用inf相连,可以发现构造成了一个简单割。然后证明最小割于最小权覆盖集一一对应。假设有最小割,如果还存在覆盖集那么存在从左边点到右边点的流量,那么不满足最大流,所以冲突;反之假设有覆盖集,我们先选出所有的点集,如果不是最大流,那么还存在流量与覆盖集定义冲突。

Code(acwing 2325)

可以删除出边和入边,所以我们可以直接拆点拆成2个图,然后求最小权覆盖集。

#include<iostream>
#include<cstring>
#include<algorithm>
#define inf 0x3f3f3f3f
using namespace std;
const int N=210,M=2e4+10;
int head[N],idx=0;
int n,m,S,T;
int q[N],d[N],cur[N];
bool st[N];
struct Edge{
    int next,to,f;
}edge[M];
void add(int a,int b,int c)
{
    edge[idx]=(Edge){head[a],b,c},head[a]=idx++;
    edge[idx]=(Edge){head[b],a,0},head[b]=idx++;
}
void dfs(int u)
{
    st[u]=true;
    for(int i=head[u];~i;i=edge[i].next)
        if(edge[i].f&&!st[edge[i].to])
            dfs(edge[i].to);
}
bool bfs()
{
    int hh=0,tt=0;
    memset(d,-1,sizeof d);
    q[0]=S,d[S]=0,cur[S]=head[S];
    while(hh<=tt)
    {
        int t=q[hh++];
        for(int i=head[t];~i;i=edge[i].next)
        {
            int ver=edge[i].to;
            if(d[ver]==-1&&edge[i].f)
            {
                d[ver]=d[t]+1;
                cur[ver]=head[ver];
                if(ver==T)  return true;
                q[++tt]=ver;
            }
        }
    }
    return false;
}
int find(int u,int limit)
{
    if(u==T)    return limit;
    int flow=0;
    for(int i=cur[u];~i&&flow<limit;i=edge[i].next)
    {
        cur[u]=i;
        int ver=edge[i].to;
        if(d[ver]==d[u]+1&&edge[i].f)
        {
            int t=find(ver,min(edge[i].f,limit-flow));
            if(!t)  d[ver]=-1;
            edge[i].f-=t,edge[i^1].f+=t,flow+=t;
        }
    }
    return flow;
}
int dinic()
{
    int res=0,flow;
    while(bfs())
        while(flow=find(S,inf))
            res+=flow;
    return res;
}
int main()
{    
    memset(head,-1,sizeof head);
    memset(st,false,sizeof st);
    scanf("%d%d",&n,&m);
    S=0,T=2*n+1;
    for(int i=1;i<=n;i++)
    {
        int w;
        scanf("%d",&w);
        add(S,i,w);
    }
    for(int i=1;i<=n;i++)
    {
        int w;
        scanf("%d",&w);
        add(n+i,T,w);
    }
    while(m--)
    {
        int a,b;
        scanf("%d%d",&a,&b);
        add(b,n+a,inf);
    }
    printf("%d\n",dinic());
    dfs(S);
    int cnt=0;
    for(int i=0;i<idx;i+=2)
    {
        int a=edge[i^1].to,b=edge[i].to;
        if(st[a]&&!st[b])   cnt++;
    }
    printf("%d\n",cnt);
    for(int i=0;i<idx;i+=2)
    {
        int a=edge[i^1].to,b=edge[i].to;
        if(st[a]&&!st[b]){
            if(a==S)    printf("%d +\n",b);
        }
    }
    for(int i=0;i<idx;i+=2)
    {
        int a=edge[i^1].to,b=edge[i].to;
        if(st[a]&&!st[b]){
            if(b==T)    printf("%d -\n",a-n);
        }
    }
    return 0;
}

最大权独立集

概述

所有的点都是无关的(不存在2个点连在一条边上),覆盖集的补集一定是独立集,反之也成立,所有点的总权值-最小权覆盖集。

证明

假设点覆盖集是v,它的补集是v‘,假设存存在2个点连同一条边同时不在v中,那么与假设不成立;反之假设独立集是v补集是v’,如果v‘不是覆盖集那么存在2点不在同一条边中,那么与v冲突。因此是互补的。

Code

直接减一下就行,水了。


文章作者: Dydong
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 Dydong !
  目录