EK最大流

可以简单理解为,不停的 b f s bfs bfs寻找增广路,找不到增广路了,就得到了最大流。每次找到增广路后,路上的最小流量记为 m i n f l o w minflow minflow,前向弧的残量减少 m i n f l o w minflow minflow,后向弧的残量增加 m i n f l o w minflow minflow,此次增广过程中增加的流量就是 m i n f l o w minflow minflow
反向边的作用在另一篇blog中提到了,是为了增加重新调整流量的机会,保证结果的最优。
因为需要同时对前向弧和后向弧操作,使用成对变换的技巧可以更方便的对这种边进行访问。
b f s bfs bfs的时候,注意第一次遇到汇点 t t t就结束增广,表示此时已经找到了一条增广路。也是因此,EK又称之为最短增广路算法。如果 b f s bfs bfs过程中,有一条弧的残量为0,那么这条弧其实已经没有任何作用了,最后增加的流量肯定是0。
因为增广过程采用的是 b f s bfs bfs,无法像 d f s dfs dfs一样在回溯的时候修改残量,因此需要记录增广路上每一个点的前驱的弧。
还需要记录增广路上的最小流量 m i n f l o w minflow minflow
剩下的就是不停的增广,每成功增广一次,最大流量 m a x f l o w = m a x f l o w + m i n f l o w maxflow=maxflow+minflow maxflow=maxflow+minflow,直到找不出增广路为止。

#include <bits/stdc++.h>
#define mem(a, b) memset(a, b, sizeof a)
using namespace std;
const int N = 310;
const int inf = 0x3f3f3f3f;
int head[N], nex[N], to[N], ed[N];
bool v[N];
int incf[N];// 增广路上的最小流量
int cnt, n, m, s, t;
int pre[N];
void pre_work(){
	cnt = 1;
	mem(head, -1);
	mem(nex, -1);
}
inline void add(int a, int b, int c){
	to[++cnt] = b, ed[cnt] = c, nex[cnt] = head[a], head[a] = cnt;
	to[++cnt] = a, ed[cnt] = 0, nex[cnt] = head[b], head[b] = cnt;
}
bool bfs(){
	queue<int > q;
	mem(v, 0);
	v[s] = 1;
	pre[s] = -1;
	incf[s] = inf;
	q.push(s);
	while (q.size()){
		int te = q.front();
		q.pop();
		for (int i = head[te]; ~i; i = nex[i]){
			if (ed[i]){
				int y = to[i];
				if (!v[y]){
					v[y] = 1;
					q.push(y);
					incf[y] = min(incf[te], ed[i]);
					pre[y] = i ^ 1;
				}
				if (y == t)return 1;
			}
		}
	}
	return 0;
}
void update(){
	int x = t;
	while (x != s){
		int te = pre[x];
		ed[te] += incf[t];
		ed[te ^ 1] -= incf[t];
		x = to[te];
	}
}
int max_flow(){
	int flow = 0;
	while (bfs()){
		update();
		flow += incf[t];
	}
	return flow;
}
int main(){
	freopen("in.in", "r", stdin);
	scanf("%d %d %d %d", &n, &m, &s, &t);
	pre_work();
	while (m--){
		int a, b, c;
		scanf("%d %d %d", &a, &b, &c);
		add(a, b, c);
	}
	printf("%d\n", max_flow());
	return 0;
}

posted @ 2022-08-03 19:07  correct  阅读(28)  评论(0)    收藏  举报