网络流征程——Dinic再详解

旧版链接



//感觉以前写的好敷衍

上次的文章绝大本分摘自别人的博客,这次我自己仔细的分析一下。

首先,简单的FolkFoukerson算法不断的增广时,可能走了太多的反向弧,即做了大量的无用功,所以我们规定了增广的一个条件——每次只增广最短的那条,那么可以保证这条增广路长度不下降,上限是O(n),如果我们简单用bfs进行增广,这就是著名的EmondKarp(EK)算法,那么整个算法的复杂度会达到(n*m^2),m可以达到n^2,即n^5,这显然很不理想。

我们再考虑一下EK算法,算法在不断BFS过程中,也重复走了太多同样的路,所以我们其实只需bfs一次,构造出一个单源最短路的层次图,那么我们接下来只需不断尝试当前层次图的最短路即可。这时由于最短路的唯一性,我们用dfs增广即可。

简单总结一下步骤:

1.BFS构造层次图,判断是否还有增广路。

2.DFS寻找增广路进行增广。

代码就不给注解了,旧版里就一个好——代码注释比较多(手动滑稽)


参考代码:

#include<cstdio>
#include<algorithm>
#include<queue>
#include<cstring>
using namespace std;
const int M=210000;
const int INF=0x3f3f3f3f;
struct Edge{
	int to,cap,next;
}e[M];
int a[M],level[M];
int ans=0;
int n,m,S,T,EdgeCnt=0;
queue<int> Q;
void addedge(int u,int v,int f){
	int p=EdgeCnt;
	e[p].to=v;e[p].cap=f;e[p].next=a[u];
	a[u]=p;
	EdgeCnt++;
}
bool bfs(int S){
	memset(level,0xff,sizeof(level));
	level[S]=0;
	Q.push(S);
	while (!Q.empty()){
		int u=Q.front();Q.pop();
		int p=a[u];
		while (p!=-1){
			int v=e[p].to;
			if (e[p].cap>0 && level[v]==-1){
				level[v]=level[u]+1;
				Q.push(v);
			}
			p=e[p].next;
		}
	}
	return level[T]>-1;
}
int dfs(int u,int left){
	if (u==T || !Left)return left;
	int p=a[u],used=0;
	while (p!=-1){
		int v=e[p].to;
		if (e[p].cap>0 && level[v]==level[u]+1){
			int w=left-used;
			w=dfs(v,min(e[p].cap,w));
			used+=w;
			e[p].cap-=w;
			e[p^1].cap+=w;
			if (used==left)return left;
		}
		p=e[p].next;
	}
	return used;
}
int main(){
	scanf("%d%d%d%d",&n,&m,&S,&T);
	memset(a,0xff,sizeof(a));
	for (int i=1;i<=m;i++){
		int u,v,f;
		scanf("%d%d%d",&u,&v,&f);
		addedge(u,v,f);
		addedge(v,u,0);
	}
	while (bfs(S)){
		ans+=dfs(S,INF);
	}
	printf("%d\n",ans);
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值