【模板】网络流-最大流(EK算法)

 每次从残留网络中找出一条从源结点到汇结点的最短路径,流选为路径中的残存容量,依据流更新残存网络(将每条边的残存容量改为当前容量减去流的大小,并添加对应的反向边,边的容量为流的大小)

重复选最短路径,更新残存网络,直到没有最短路径为止

此时的流累加和即为最大流

此算法的精华也就在于用建立反向边而代替了回溯的过程,大大减少了时间复杂度,

为何建立反向边正确呢?

如果增广路中有反向边,其实就是把该正向边边对应的流量“退“了回去,使边拥有”反悔“的机会。


例题:POJ 1273 http://poj.org/problem?id=1273 


#include <iostream>
#include <cstring>
#include <iomanip>
#include <algorithm>
#include <queue>
#include <stack>
#include <vector>
#include <set>
#include <map>
#include <cmath>
#include <cstdio>
//#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
//typedef __int128 bll;
const ll maxn = 105+100;
const ll mod = 1e9+7;
const ld pi = acos(-1.0);
const ll inf = 1e18;
const ld eps = 1e-5;
//const ld e = exp(1);

ll n,m;
ll G[maxn][maxn],pre[maxn]; //G用来保存残余网络,per存储当前节点的前驱节点 
bool flag[maxn]; //判断是否在队列中 

bool bfs(ll be,ll en)   //寻找增广路 
{
	queue<ll>Q;
	memset(pre,-1,sizeof(pre)); 
	memset(flag,false,sizeof(flag));
	
	Q.push(be);
	flag[be] = true;
	pre[be] = be;
	
	while(!Q.empty())
	{
		ll tmp = Q.front();
		Q.pop();
		
		if(tmp == en)
				return true;
				
		for(ll i = 1; i <= n; i++)
		{
			if(flag[i] == false && G[tmp][i] > 0)
			{
				flag[i] = true;
				pre[i] = tmp;
				
				Q.push(i);
			}
		}	
	}
	
	return false;
}

ll EK(ll be,ll en)
{
	ll flow = 0,mi;
	
	while(bfs(be,en))
	{
		mi = inf;
		
		for(ll i = en; i != be; i = pre[i])
		{
			mi = min(mi,G[pre[i]] [i] );     //寻找最小值 
		}
		
		for(ll i = en; i != be; i = pre[i])
		{
			G[ pre[i] ][i] -= mi;    //更新正向边 
			G[i][ pre[i] ] += mi;   //建立反向边 
		}
		
		flow += mi;
	}
	return flow;
}

int main()
{	
    ios::sync_with_stdio(false);
    cin.tie(0),cout.tie(0);
	
	while( cin >> m >> n )
	{
		memset(G,0,sizeof(G));
		//cin >> m >> n;
		
		for(ll i = 1; i <= m; i++)
		{
			ll x,y,z;
			cin >> x >> y >> z;
			
			G[x][y] += z;   //有可能一条边出现多次 
		}	
		
		cout << EK(1,n) << endl;
		
	}
	
	return 0;
}

猜你喜欢

转载自blog.csdn.net/Whyckck/article/details/89138375