欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

网络流入门

程序员文章站 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还未被编号,则dep[q]=dep[p]+1dep[q]=dep[p]+1
显而易见,从层数高的地方搜索到层数低的地方时间可以避免重复的搜索。

但是如何处理A-B这种情况呢?

当搜索到A时,将原有的dep[A]++dep[A]++,然后dep[A]=3dep[B]=2dep[A]=3,dep[B]=2,就可以搜索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; 
}