【模板】网络最大流

link

终于过了这个奇怪的网络流了。一直感觉那个什么Dinic算法很奇怪,还要什么bfs分层,今天花了一下午的时间终于搞懂了Dinic是什么鬼。

Dinic是一个求解网络最大流的算法。我目前所掌握的只是小优化版本的Dinic,至于数据加强版我过不了(太弱了)。这个算法和前面二分图最大匹配算法是一个内核,都是在思考假如我们已经有一个方案了,如何在不破坏这个方案的基础上尽量多地塞新的元素进去(新的元素就是所谓的增广路)。二分图上增广路是一个交错杂居的路径,而网络流中的增广路是一条从S到T的一个流量不为0的合法轨迹。如何求解?首先可以使用朴素的想法,假如我们已经有一个网络流了(或者叫做残量网络流),思考如何能找寻一条增广路呢?有两种途径。一是自己开辟一条新的道路通向汇点,另一种是让其它道路的流量滚回去,空出来的流量自己填上。第一种很好思考,第二种应该怎么办呢?思考一下,对比一下添加这条增广路前后这个网络的变化有哪些。很明显原先那条被占的路上的流量不会有变化,变化的是挨了打之后重新寻找出路的那股旧流开辟的道路罢了。既然变化量只有那个,那么完全可以把这个过程看成是当前增广路在开辟新天地,实现上只需要建反向边就可以了。

讲解里那些术语把我搞蒙了,不就是那个意思吗。

有个bfs对朴素算法的优化。为什么我不知道,但确实可以优化朴素的Dinic。另外这里一定要手写一个队列,用std会死得很难看。代码层面,bfs很简单不说,dfs就是枚举出边,验证层数关系,累加流量即可。

#include<cstdio>
#include<cstring>
//#define zczc
#define int long long
using namespace std;
const int N=210;
const int M=5010;
const int inf=1e12;
inline void read(int &wh){
    wh=0;int f=1;char w=getchar();
    while(w<'0'||w>'9'){if(w=='-')f=-1;w=getchar();}
    while(w<='9'&&w>='0'){wh=wh*10+w-'0';w=getchar();}
    wh*=f;return;
}
inline int min(int s1,int s2){
	return s1<s2?s1:s2;
}

int m,n,s,t;
struct edge{
	int t,v,next;
}e[M<<1];
int head[N],esum=1;
inline void add(int fr,int to,int val){
	esum++;e[esum].t=to;e[esum].v=val;e[esum].next=head[fr];
	head[fr]=esum;return;
}

int d[N],q[N],l,r;
bool bfs(){
	memset(d,0,sizeof(int)*(m+1));
	q[l=r=1]=s,d[s]=1;
	while(l<=r){
		int now=q[l++];
		for(int i=head[now],th;i;i=e[i].next){
			if(e[i].v&&d[th=e[i].t]==0)d[th]=d[now]+1,q[++r]=th;
		}
	}
	return d[t]>0;
}

int dfs(int wh,int val){
	if(wh==t)return val;int cost=0;
	for(int i=head[wh],th;i;i=e[i].next){
		if(e[i].v==0||d[th=e[i].t]!=d[wh]+1)continue;
		int now=dfs(th,min(val,e[i].v));
		e[i].v-=now,e[i^1].v+=now,val-=now,cost+=now;
	}
	return d[wh]=cost==0?0:d[wh],cost;
}

signed main(){
	
	#ifdef zczc
	freopen("in.txt","r",stdin);
	#endif
	
	int s1,s2,s3;
	read(m);read(n);read(s);read(t);
	for(int i=1;i<=n;i++){
		read(s1);read(s2);read(s3);
		add(s1,s2,s3);add(s2,s1,0);
	}
	
	int ans=0;
	while(bfs())ans+=dfs(s,inf);
	printf("%lld",ans);
	
	return 0;
}

update.2022.02.08
check函数压行版

bool check(){
	memset(d,0,sizeof(d));q[l=r=1]=s;d[s]=1;int wh;
	while(l<=r)
		for(int i=head[wh=q[l++]],th;i;i=e[i].next)
			if(e[i].v!=0&&d[th=e[i].t]==0)q[++r]=th,d[th]=d[wh]+1;
	return d[t];
}
posted @ 2022-02-07 17:07  Feyn618  阅读(31)  评论(0编辑  收藏  举报