网络流入门——最大流与最小割

网络流的定义

网络流(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 SAT S − B − T S-B-T SBT两条路可以走,总流量是 2 2 2

下面有一个错误示范:走完S-A-B-T后S-A与B-T的路径均已满,不能再走,此时程序会产生错误输出1。
解决方案

既然程序走了一条错误的路,那我们就给它一个反悔的机会——反向建边。

反向建边以后,就会变成这样:

然后再走 S − A − B − T S-A-B-T SABT,就会变成:

然后就可以走 S − B − A − T S-B-A-T SBAT这条路了,答案是 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]=3dep[B]=2,就可以搜索 A − B A-B AB了。

优化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; 
}
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值