最小割
是一个边集,去掉其中的边之后源点汇点无法再连通,且这个边集的容量之和最小
最大流最小割定理
一个网络中最大流量等于最小割中边的容量之和,即最大流
=
=
=最小割
证
- 法一
假设最小割 < < <最大流,割去这些边后还能找到增广路,源点汇点仍然连通
故最小割 ≥ \geq ≥最大流,取最小的割,有最大流 = = =最小割 - 法二
(我自己YY出来的)
对于每一条源点流向汇点的路径,其流量一定等于路径中容量最小的边,此边一定流满
割去所有的这样的边,可以得到最小割
而根据法二,容易得到最小割的求法
得到最大流后,沿残量网络BFS,构建层次图(走DFS建搜索树也行,反正把源点一边打上标记就行了)
由于割去了路径中最小的边,这条边会连接残量网络能到达的点和不能到达的点
而这样的边就是割边
网络流问题中容易出现边点转化问题
点拆成两个点,中间一条边体现点信息
边中间插个点,体现边的信息
最小割中边权赋 + ∞ +\infin +∞有防止割断的含义
例 黑手帮
对于一个点,
u
→
u
+
n
u\to u+n
u→u+n,权值赋为点权
对于两个点,要保证
u
→
v
u\to v
u→v要消耗
u
u
u的边权,于是
u
→
v
u\to v
u→v边权赋为
0
0
0,
u
+
n
→
v
u+n\to v
u+n→v边权赋为
∞
\infin
∞,这样只能走
u
→
u
+
n
→
v
u\to u+n\to v
u→u+n→v
艹
这题数据太水了
我汇点都没+n
都有98pts 😄
#include<bits/stdc++.h>
using namespace std;
#define in Read()
int in{
int i=0,f=1;char ch=0;
while(!isdigit(ch)&&ch!='-') ch=getchar();
if(ch=='-') ch=getchar();
while(isdigit(ch)) i=(i<<1)+(i<<3)+ch-48,ch=getchar();
return i*f;
}
const int INF=2147483647;
const int N=4e2+5;
const int M=4e4+5;
int n,m,s,t;
int tot,first[N],nxt[M<<1],aim[M<<1],wei[M<<1];
int d[N];
bool vis[N];
void ljb(int u,int v,int w){
++tot;
nxt[tot]=first[u];
first[u]=tot;
wei[tot]=w;
aim[tot]=v;
return;
}
bool BFS(){
memset(d,0,sizeof(d));
queue<int>q;
q.push(s),d[s]=1;
while(!q.empty()){
int u=q.front();q.pop();
for(int e=first[u];e;e=nxt[e]){
int v=aim[e];
if(wei[e]&&!d[v]){
q.push(v),d[v]=d[u]+1;
if(v==t) return true;
}
}
}
return false;
}
int dinic(int u,int flow){
if(u==t) return flow;
int rest=flow,tmp;
for(int e=first[u];e&&rest;e=nxt[e]){
int v=aim[e];
if(wei[e]&&d[v]==d[u]+1){
tmp=dinic(v,min(rest,wei[e]));
if(!tmp) d[v]=0;
wei[e]-=tmp;
wei[e^1]+=tmp;
rest-=tmp;
}
}
return flow-rest;
}
void DFS(int u){
vis[u]=true;
for(int e=first[u];e;e=nxt[e]){
int v=aim[e];
if(vis[v]||!wei[e]) continue;
DFS(v);
}
return;
}
int main(){
n=in,m=in,s=in,t=in+n,tot=1;
for(int i=1;i<=n;++i){
int w=in;
ljb(i,i+n,w);
ljb(i+n,i,0);
}
for(int i=1;i<=m;++i){
int u=in,v=in;
ljb(u+n,v,INF);ljb(v,u+n,0);
ljb(v+n,u,INF);ljb(u,v+n,0);
}
while(BFS()) while(dinic(s,INF));
DFS(s);
vector<int>ans;
for(int e=1;e<=tot;e+=2){
if(vis[aim[e]]&&!vis[aim[e^1]])
ans.push_back(aim[e]);
}
sort(ans.begin(),ans.end());
for(int i=0;i<ans.size();++i) printf("%d ",ans[i]);
return 0;
}