P3376 【模板】网络最大流

首先我们都已经知道了网络的定义和各种各样的东西。

最大流就是从源点到汇点的最多能流的流量,可以类比二分图的最大匹配。考虑怎么来求她。

Dinic算法

建图没话说,一条正弧一条反弧。

void adde(int u,int v,int cap){
	edges.pb({u,v,cap,0});
	G[u].pb(edges.size()-1);
	edges.pb({v,u,0,0});
	G[v].pb(edges.size()-1);
}

首先我们可以用找增广路的思路,每次去找在当前基础上有没有更优的(更多流量的)方案。我们称每次找到的这个最大的量为 可改进量

我们可以用 \(dfs\) 的思想去找这个可改进量,但是由于 \(dfs\) 是走路不看路的,你得给她建一个盲道,这就是层次图了。由于 \(bfs\) 是按层次来搜的,所以我们可以先搜一遍,求出每个点所对应的层次,这样 \(dfs\)

bool BFS(){
	memset(dis,-1,sizeof(dis));
	queue<int> q;
	q.push(src);
	dis[src]=0;
	while(!q.empty()){
		int u=q.front();q.pop();
		for(int i=0;i<G[u].size();i++){
			Edge e=edges[G[u][i]];
			if(dis[e.to]==-1&&e.cap>e.flow){
				dis[e.to]=dis[u]+1;
				q.push(e.to);
			}
		}
	}
	return dis[dst]!=-1;
}

这个还可以顺带判断网络还能不能再改进了。

有个这个后,我们就可以愉快的用 \(dfs\)

int dfs(int u,int a){//u为当前点,a为当前最大可用的流量,返回值为可改进量
	if(u==dst||!a) return a;//如果走到汇点dst或当前没有可用的流量了,就返回
	int flow=0;
	for(int &i=cur[u];i<G[u].size();i++){//当前弧优化(后面会讲到)
		Edge &e=edges[G[u][i]];
		if(dis[u]+1==dis[e.to]){//按层次图来走
			int f=dfs(e.to,min(a,e.cap-e.flow));//a每次都要取min
			e.flow+=f;
			edges[G[u][i]^1].flow-=f;//一条弧流量增加f,另一条弧就要减f,用于反悔
			flow+=f;
			a-=f;
			if(!a) break;//没可用流量就break掉
		}
	}
	return flow;//当前点u的可改进量
}

如果没有当前弧优化的话,我们可能会重复走一些点。但是对于一个点来说,如果求出了她的可改进量,那么就相当于把这个点可用的潜能全榨干了,下一次再重算就没有意义了,所以我们对于每一点,我们都保证再这一次的 \(dfs\)

求最大流部分

int maxflow(int s,int t){
	src=s,dst=t;//源点和汇点
	int flow=0;
	while(BFS()){
		memset(cur,0,sizeof(cur));//当前弧优化标记清零
		flow+=dfs(s,INF);//从s点出发有INF的流量可以用
	}
	return flow;
}

\(code\)

#include<bits/stdc++.h>
#define int long long
#define pb push_back
using namespace std;
inline int read(){
	int ans=0,f=1;char ch=getchar();
	while(!isdigit(ch)){if(ch=='-') f=-f;ch=getchar();}
	while(isdigit(ch)){ans=(ans<<3)+(ans<<1)+ch-48;ch=getchar();}
	return ans*f;
}
const int N=5005,M=205,INF=1e9;
int n,m,s,t;
int src,dst;
int cur[M];
int dis[M];
struct Edge{
	int from,to,cap,flow;
};
vector<Edge> edges;
vector<int> G[M];
void adde(int u,int v,int cap){
	edges.pb({u,v,cap,0});
	G[u].pb(edges.size()-1);
	edges.pb({v,u,0,0});
	G[v].pb(edges.size()-1);
}
bool BFS(){
	memset(dis,-1,sizeof(dis));
	queue<int> q;
	q.push(src);
	dis[src]=0;
	while(!q.empty()){
		int u=q.front();q.pop();
		for(int i=0;i<G[u].size();i++){
			Edge e=edges[G[u][i]];
			if(dis[e.to]==-1&&e.cap>e.flow){
				dis[e.to]=dis[u]+1;
				q.push(e.to);
			}
		}
	}
	return dis[dst]!=-1;
}
int dfs(int u,int a){
	if(u==dst||!a) return a;
	int flow=0;
	for(int &i=cur[u];i<G[u].size();i++){
		Edge &e=edges[G[u][i]];
		if(dis[u]+1==dis[e.to]){
			int f=dfs(e.to,min(a,e.cap-e.flow));
			e.flow+=f;
			edges[G[u][i]^1].flow-=f;
			flow+=f;
			a-=f;
			if(!a) break;
		}
	}
	return flow;
}
int maxflow(int s,int t){
	src=s,dst=t;
	int flow=0;
	while(BFS()){
		memset(cur,0,sizeof(cur));
		flow+=dfs(s,INF);
	}
	return flow;
}
signed main(){
	n=read(),m=read(),s=read(),t=read();
	for(int i=1;i<=m;i++){
		int x=read(),y=read(),v=read();
		adde(x,y,v);
	}
	printf("%lld",maxflow(s,t));
	return 0;
}