网络流入门
程序员文章站
2024-03-04 15:15:17
...
网络流的定义
网络流(network-flows)是一种类比水流的解决问题方法,与线性规划密切相关。
例题
水要从S流到T,一共n个点,m根管道,每条管道有一个最大容量,求最多有多少水能流到T。
n<=100000,m<=800000
以下面这张图为例:
有S-A-T与S-B-T两条路可以走,总流量是2。
下面有一个错误示范:走完S-A-B-T后S-A与B-T的路径均已满,不能再走,此时程序会产生错误输出1。
解决方案
既然程序走了一条错误的路,那我们就给它一个反悔的机会——反向建边。
反向建边以后,就会变成这样:
然后再走S-A-B-T,就会变成:
然后就可以走S-B-A-T这条路了,答案是2。
优化1
从S走到T,如果按照一条边一条边的方法走,时间复杂度会很高,所以我们需要一些优化。
如图所示:
从汇点T开始往回用bfs搜索,若由P点搜到Q点且Q还未被编号,则。
显而易见,从层数高的地方搜索到层数低的地方时间可以避免重复的搜索。
但是如何处理A-B这种情况呢?
当搜索到A时,将原有的,然后,就可以搜索A-B了。
优化2
当进行bfs时,对于每个dep的值,我们用一个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;
}
上一篇: 1.9 java数组的基本使用方法
下一篇: java小白学习记录:数组的基本介绍