最小割
最小割
定义:
给定一个网络 \(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的作物
#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;
}