最小割

最小割

定义:

给定一个网络 \(G=(V,E)\) ,源点为 \(s\) , 汇点为 \(t\) ,若一个边集被删去之后,源点和汇点不在联通,则称该边集为网络的割。

边的容量之和最小的割叫做网络流的最小割。

定理:

任何一个网络的最大流量等于最小割中边的容量之和,简称为:

最大流=最小割

过程:

求出最大流后,从原点开始沿残量网络 \(BFS\) ,标记能够到达的点, \(E\) 中所有连接 已经标记的点 \(x\)未标记点 \(y\) 的边 \((x,y)\) 构成该网络的最小割。

其实代码和最大流一样.....

求的东西:

最小割:

直接 \(Dinic\) 输出最大流即可。

方案:

通过源点开始 \(dfs\) ,每次走残量大于 \(0\) 的边,找到所有 \(S\) 点集内的点:

void dfs(int x){
    vis[x]=1;
    for(int i=head[x];i;i=nxt[i]){
        int y=ver[i];
        if(!vis[y]&&edge[i]) dfs(y);
    }
}

割边数量:

只要将每条边的容量变为 \(1\) ,然后重新跑 \(Dinic\) 即可。

问题模型:

\(n\) 个物品和两个集合 \(A,B\) ,一个物品放入 \(A\) 花费 \(a_i\),放入 \(B\) 会花费 \(b_i\);有若干个 \(u_i,v_i,w_u\) 限制条件,如果 \(u_i\)\(v_i\) 同时不在一个集合会花费 \(w_i\)。每个物品必须且只能属于一个集合,求最小的代价。

这是一个经典的 二者选其一 的最小割题目。我们对于每个集合设置源点 \(s\) 和汇点 \(t\),第 \(i\) 个点由 \(s\) 连一条容量为 \(a_i\) 的边、向 \(t\) 连一条容量为 \(b_i\) 的边。对于限制条件 \(u,v,w\),我们在 \(u,v\) 之间连容量为 \(w\) 的双向边。

注意到当源点和汇点不相连时,代表这些点都选择了其中一个集合。

如果将连向 \(s\)\(t\) 的边割开,表示不放在 \(A\)\(B\) 集合,如果把物品之间的边割开,表示这两个物品不放在同一个集合。

最小割就是最小花费

最小割就是最小花费。

例题:

P1361 小M的作物

// P1361 小M的作物
#include<bits/stdc++.h>
using namespace std;
#define ll long long 
const int inf=0x3f3f3f3f,N=10005,M=4e6+5;
int nxt[M],ver[M],tot=1,edge[M],head[N];
int n,m,s,t,cnt;
int dep[N],incf[N],pre[N],cur[N];

void add(int x,int y,int z){
    ver[++tot]=y; edge[tot]=z; nxt[tot]=head[x]; head[x]=tot;
    ver[++tot]=x; edge[tot]=0; nxt[tot]=head[y]; head[y]=tot;
}

bool bfs(){
    memset(dep,0,sizeof(dep)); queue<int> q; q.push(s); dep[s]=1;
    while(!q.empty()){
        int x=q.front(); q.pop();
        for(int i=head[x];i;i=nxt[i]){
            int y=ver[i],z=edge[i];
            if(dep[y]||!z) continue;
            q.push(y);
            dep[y]=dep[x]+1;
            if(y==t) return 1;
        }
    }
    return 0;
}

int dinic(int x,int flow){//flow表示限制流量
    if(x==t) return flow;
    int totflow=0;//从这个点总共可以增广多少流量
    for(int i=cur[x];i;i=nxt[i]){
        cur[x]=i;
        int y=ver[i],z=edge[i];
        if(dep[y]!=dep[x]+1||!edge[i]) continue;
        int canflow=dinic(y,min(flow,z));//表示这条增广路上能增加多少流量
        edge[i]-=canflow; edge[i^1]+=canflow;
        totflow+=canflow;//总共增加多少流量
        flow-=canflow;
        if(flow<=0) break;//当前点已经没有流量
    }
    return totflow;
}
int main(){
    cin>>n; s=n+1,t=s+1;
    for(int i=1,x;i<=n;i++){
        scanf("%d",&x); cnt+=x; add(s,i,x); add(i,s,0);
    }
    for(int x,i=1;i<=n;i++){
        scanf("%d",&x),cnt+=x,add(i,t,x),add(t,i,0);
    }
    scanf("%d",&m);
    for(int i=1;i<=m;i++){
        int T,tota,totb;
        scanf("%d%d%d",&T,&tota,&totb);
        cnt+=tota+totb;
        add(s,n+2+i,tota); add(n+2+i,s,0);
        add(n+2+i+m,t,totb); add(t,n+2+i+m,0);
        for(int x,j=1;j<=T;j++){
            scanf("%d",&x);
            add(n+2+i,x,inf);
            add(x,n+2+i,0);
            add(x,n+2+i+m,inf);
            add(n+2+i+m,x,0);
        }
    }
    int flow=0,maxflow=0;
    while(bfs()){
        memcpy(cur,head,sizeof(head));
        maxflow+=dinic(s,inf);
    } 
    printf("%d\n",cnt-maxflow);
    system("pause");
    return 0;
}

posted @ 2021-10-10 22:02  Evitagen  阅读(571)  评论(0编辑  收藏  举报