[博客迁移][算法] 最大流EdmondsKarp算法

103 阅读3分钟

from 最大流EdmondsKarp算法


问题阐述:

告诉你n个节点,以及m条边(有向边),已知每条边的容量和当前流量,求从第一个节点到第n个节点的最大流量。

求法:

根据已知的容量和流量可以计算出每条边目前还可以通过的流量,然后宽搜出一条从1到n的路(还可通过的流量必须大于0才认为这条边存在),找到这条路上最小的可通过的流量,然后更新答案以及这些边上的流量。循环操作直到不存在一条路从1到n。

这样做有一个问题,那就是,如果有多条路从1到达n,而我们可以选择其中的一些路来计算流量,那么如何选取就很重要。

举个例子

有4个结点,5条路,默认当前流量都为0,如果不为0,只需要把容量相应减少即可 起点,终点,容量 1 2 1 1 3 3 2 3 2 2 4 3 3 4 1

如果搜索到1-2-3-4这条路,然后将相应边更新,就会发现,不存在其他的从1到n的路了,然而答案显然不是1,因为1-3-4与1-2-4可以组成流量2。

因此,EK算法引入了一个错误纠正机制,正如上面的例子,其实找到1-2-3-4这条路是错误的,不应该选择这条路,那么怎么办呢,更新1-2-3-4这些边的同时,加入一些反向边,1-2-3-4这条路,把每条边的容量减1的同时,让反向边的容量加1,这样做之后,再去搜索从1到n的路就会发现多出来了一条,1-3-2-4,这样流量就变成2了。

走1-3-2-4这条路的本质就是:先走1-2-3-4这条路,然后发现出错了,因此不走1-2-3-4这条路,转而走1-2-4与1-3-4这两条路,走3-2这条边其实就是把之前的1-2-3-4这条路断开来,剩下1-2与3-4,然后与1-3-2-4中的1-3、2-4组合,形成新的路:1-2-4、1-3-4。

HDU-1532代码:

// @Team    : nupt2017team16
// @Author  : Zst
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <string>
#include <vector>
#include <queue>
#include <cmath>
#include <algorithm>
#include <map>
using namespace std;

#define LL 			long long
#define MOD 		1000000007
#define MEM(a,x) 	memset(a,x,sizeof(a))
#define FILL(a,n,x) fill(a,a+n,x)
#define INF 		0x3f3f3f3f
#define pb 			push_back
#define FOR(i,a,b) 	for( int i = ( a ); i <= ( b ); ++i )
#define WHILE() 	int T;scanf( "%d", &T );while( T-- )

const int N = 200+7;

int n, m;

int C[N][N];
int pre[N];
bool vis[N];

bool bfs() {
	MEM( pre, -1 );
	pre[1] = 1;
	queue<int> q;
	q.push( 1 );
	MEM( vis, false );
	vis[1] = true;
	while( !q.empty() ) {
		int u = q.front();
		q.pop();
		FOR( i, 1, n ) {
			if( C[u][i] > 0 && vis[i] != true ) {
				q.push( i );
				vis[i] = true;
				pre[i] = u;
				if( i == n ) {
					return true;
				}
			}
		}
	}
	return false;
}

int solve() {
	int ans = 0;
	while( bfs() ) {
		int mins = INF;
		for( int i = n; i != 1; i = pre[i] ) {
			mins = min( mins, C[pre[i]][i] );
		}
		for( int i = n; i != 1; i = pre[i] ) {
			C[pre[i]][i] -= mins;
			C[i][pre[i]] += mins;
		}
		ans += mins;
	}
	return ans;
}



int main()
{
	// freopen( "1532.txt", "r", stdin );
	while( scanf( "%d%d", &m, &n ) != EOF ) {
		MEM( C, 0 );
		int u, v, w;
		FOR( i, 0, m-1 ) {
			scanf( "%d%d%d", &u, &v, &w );
			C[u][v] += w;
		}
		printf( "%d\n",solve() );
	}
	return 0;
}