网络流的定义
网络流(network-flows)是一种类比水流的解决问题方法,与线性规划密切相关。
最大流例题
水要从 S S S流到 T T T,一共 n n n个点, m m m根管道,每条管道有一个最大容量,求最多有多少水能流到 T T T。
n < = 10000 , m < = 80000 n<=10000,m<=80000 n<=10000,m<=80000
以下面这张图为例:
有
S
−
A
−
T
S-A-T
S−A−T与
S
−
B
−
T
S-B-T
S−B−T两条路可以走,总流量是
2
2
2。
下面有一个错误示范:走完S-A-B-T后S-A与B-T的路径均已满,不能再走,此时程序会产生错误输出1。
解决方案
既然程序走了一条错误的路,那我们就给它一个反悔的机会——反向建边。
反向建边以后,就会变成这样:
然后再走
S
−
A
−
B
−
T
S-A-B-T
S−A−B−T,就会变成:
然后就可以走
S
−
B
−
A
−
T
S-B-A-T
S−B−A−T这条路了,答案是
2
2
2。
优化1
从
S
S
S走到
T
T
T,如果按照一条边一条边的方法走,时间复杂度会很高,所以我们需要一些优化。
如图所示:
从汇点T开始往回用
b
f
s
bfs
bfs搜索,若由
P
P
P点搜到
Q
Q
Q点且
Q
Q
Q还未被编号,则
d
e
p
[
q
]
=
d
e
p
[
p
]
+
1
dep[q]=dep[p]+1
dep[q]=dep[p]+1。
显而易见,从层数高的地方搜索到层数低的地方时间可以避免重复的搜索。
但是如何处理A-B这种情况呢?
当搜索到A时,将原有的 d e p [ A ] + + dep[A]++ dep[A]++,然后 d e p [ A ] = 3 , d e p [ B ] = 2 dep[A]=3,dep[B]=2 dep[A]=3,dep[B]=2,就可以搜索 A − B A-B A−B了。
优化2
当进行 b f s bfs bfs时,对于每个 d e p dep dep的值,我们用一个 c n t cnt cnt数组来记录其出现次数,当某个 c n t cnt cnt的值等于0时,即代表搜索完毕,可以跳出循环。
最小割
最小割等于最大流 证明
代码
#include<bits/stdc++.h>
using namespace std;
int n,m,s,t;
int tot=1,first[200005],nxt[200005],to[200005],w[200005],dep[200005],cnt[200005];
int Read(){
int x=0,f=1;
char ch=getchar();
while(!isdigit(ch)) {if(ch=='-') f=-1; ch=getchar();}
while(isdigit(ch)) {x=(x<<3)+(x<<1)+ch-'0'; ch=getchar();}
return x*f;
}
void Add(int x,int y,int z){ //建边
nxt[++tot]=first[x];
first[x]=tot;
to[tot]=y;
w[tot]=z;
}
void Bfs(int t){ //求dep与cnt数组
memset(dep,0xff,sizeof(dep));
dep[t]=0;
cnt[0]=1;
queue<int> q;
q.push(t);
while(!q.empty()){
int u=q.front();
q.pop();
for(int e=first[u];e;e=nxt[e]){
if(dep[to[e]]==-1){
dep[to[e]]=dep[u]+1;
++cnt[dep[to[e]]];
q.push(to[e]);
}
}
}
}
int mf=0;
int dfs(int p,int f){ //求最大流
if(p==t){
mf+=f;
return f;
}
int u=0;
int e;
for(e=first[p];e;e=nxt[e]){
if(w[e]&&dep[to[e]]==dep[p]-1){
int uu=dfs(to[e],min(w[e],f-u));
if(uu){
w[e]-=uu;
w[e^1]+=uu;
u+=uu;
}
if(u==f) return u;
}
}
if(!--cnt[dep[p]]){
dep[s]=n+1;
}
++cnt[++dep[p]];
return u;
}
signed main(){
n=Read(),m=Read(),s=Read(),t=Read();
for(int i=1;i<=m;i++){
int U=Read(),V=Read(),W=Read();
Add(U,V,W); //正向建边
Add(V,U,0); //反向建边
}
Bfs(t);
while(dep[s]<n){
dfs(s,0x3fffffff);
}
cout<<mf<<endl;
return 0;
}