网络流最大流——EK

先说一下网络流最大流是什么:
一个有向图,每条边有一个流量限制,我们需要求出从源点s到汇点t的最大流量。例子:

这个图的最大流就是20+20=40。
概念:
残量网络:根据原图所建的,经过通流量操作每一条边的剩余流量限度为它的总流量限度减去它所通过的流量。
增广路:残量网络中一条从s到t的路径可以使得s到t的最大流增大。
EK算法思路:
1.通过bfs找增广路,增广路中的边不为0且增广路的最大流量是这条路径上的剩余流量限度最小的边的剩余流量限度(一次bfs只能找一条增广路)。
2.每找到一条增广路,就将这条增广路上的边的剩余流量限度都减去这条增广路能通过的最大流量,并将最大流加上这条增广路的最大流量。
3.知道找不到增广路,我们得到的最大流就是s到t的最大流。
但是这里会出现一个很大的问题:

如果我们bfs找到的第一条增广路是1到2到3到4,则它不会再找到新的增广路,最大流是10,但实际上可以先找到第一条增广路1到2到4,再找到第二条增广路1到3到4,最大流是20。
下面就需要解决这个问题,这里也是我认为的网络流的比较难的一点:
可以给这个网络流的每一条边加一个反向边相互匹配,而它的剩余流量限度就是它所对应的边的所通过的流量,即这两条对应边的剩余流量限度之和就是这条原边的总流量限度,每修改一条边时同时将它的对应边一并修改(用邻接表存边时将原边存为奇数下标,它的对应边的下标是原边下标+1)。
那么上面的问题就可以解决了:

先将每条边都添加一个对应边。

如果出现先找到的增广路是1到2到3到4。

这样就还能找到一条新的增广路是1到3到2到4,最终求出的最大流是20。
下面是代码:

点击查看代码
#include<iostream>
#include<queue>
#define INF 0x3f3f3f
#define int long long
using namespace std;
int n,m,s,t;
struct node{
	int from;
	int to;
	int nxt;
	int val;
}edge[10010];
int head[210],tot;
void addedge(int u,int v,int w){
	edge[++tot].to=v;
	edge[tot].from=u;
	edge[tot].val=w;
	edge[tot].nxt=head[u];
	head[u]=tot;
}
int ans;
queue<int> q;
bool bfs(){
	while(!q.empty()){
		q.pop();
	}
	int minn[210];
	int pre[210];
	for(int i=1;i<=n;i++){
		pre[i]=0;
		minn[i]=INF;
	}
	pre[s]=-1;
	q.push(tot);
	while(!q.empty()){
		int x=q.front();
		q.pop();
		int u=edge[x].to;
		for(int i=head[u];i;i=edge[i].nxt){
			int v=edge[i].to;
			if(pre[v]==0&&edge[i].val!=0){
				pre[v]=i;
				minn[v]=min(edge[i].val,minn[u]);
				q.push(i);
			}
			if(v==t&&edge[i].val!=0){
				int dian=t;
				while(dian!=s){
					edge[pre[dian]].val-=minn[t];
					if(pre[dian]%2==1){
						edge[pre[dian]+1].val+=minn[t];
					}
					else{
						edge[pre[dian]-1].val+=minn[t];
					}
					dian=edge[pre[dian]].from;
				}
				ans+=minn[t];
				return 1;
			}
		}
	}
	return 0;
}
signed main(){
	cin>>n>>m>>s>>t;
	for(int i=1;i<=m;i++){
		int u,v,w;
		cin>>u>>v>>w;
		addedge(u,v,w);
		addedge(v,u,0);
	}
	edge[++tot].to=s;
	while(bfs());
	cout<<ans;
	return 0;
}
posted @ 2022-03-28 19:39  zzzzzz2  阅读(333)  评论(0编辑  收藏  举报