最大流模板(Edmonds-Karp)

最近在看网络流,看了算法导论不是很懂。。不知道书里那个图是不是错了,搞得我有点混乱;

然后就从网上查下资料,翻了几个大神的博客,就搞懂了个大概是怎么回事;网络流里有很多

算法,因为是入门,所以就写了个EK(Edmonds-Karp)求最大流的算法,顺便做了个模板题;

写的时候还行,大概思路没什么问题,就是实现的时候有点小错误,下面是本人学习中的一些理解:


Edmonds-Karp求最大流的算法核心思想就是不断找增广路,每找到一条增广路,记录增广路中

边流量最少的值d,然后让当前找到增广路中的每一条正向弧减去d,让反向弧加上d,当不再找

到增广路的时候,就证明当前的总流量已经是最大流;至于怎样找增广路。。一致认为BFS是最好

的方法了。。。


增广路(摘自NOCOW):设f 是一个可行流,W是发点到收点的一条有向道,如果W满足下列条件,称之为关于可行流f的一条可增广道
(又称可扩道):
1. 每条正向弧是非饱和弧,
2. 每条反向弧是非零流弧.


做完题之后,发现如果不加反向弧好像也可以算出答案,但是交上去又WA,然后百度搜了下


为什么要有反向边或者残余图?

前面我们确定了一条路径的最小流量之后,我们实际上可以把这部分流量从图中删去(这个可以看做减治的过程):因为这个路径以后是不会更改的,而且路径之间互不影响。但是也会出现一定的问题:结果往往不是最优的。反向边和残余图可以解决这一问题。

最近又看了算法导论之后,看到了个可以作为不加反向弧而得不到最优解的反例,如下图



第一张图是最原始的图,值为边的容量,第二张图是找出s->v1->v2->t增广路后不加反向弧的残余图;明显的,如果不加反向弧,v1->v2一旦成为饱和弧后,就不能恢复,而这样最后算出来的流值是199,但是正确的最大流值是200,所以不加反向弧不能保证得到的是最优解(这算是证明么- -!);


POJ 1273(Drainage Ditches),可以用来测试下代码写得对不对,第一次做网络流的题,纪念下。。

#include <cstdio>
#include <cstring>
#include <queue>
#include <algorithm>
using namespace std;

const int inf=0xfffff;
const int N = 330;
int maxflow, pre[N], dp[N][N];

void Edmonds_Karp(int start, int end, int m){
	while(1){
		queue<int> p;
		int minflow = inf;
		p.push(1);				  //源点为1,进队 
		memset(pre, 0, sizeof(pre));//初始化增广路径数组,题目中的顶点是从1开始的 
		while(!p.empty()){		//bfs找增广路 
			int u = p.front();
			p.pop();
			if(u == end)			 
				break;
			for(int i = 1;i <= m;i++)
				if(dp[u][i] > 0&&pre[i] == 0){ //pre[i]除了记录当前顶点的父亲,还记录当前顶点有没被访问过 
					pre[i] = u;				 
					p.push(i);
				}	 
		}
		if(pre[end] == 0)					//顶点的父亲为空,表示找不到增广路,很容易理解吧。。 
			break;
		for(int i = end;i != start;i = pre[i])	//找出增广路中最小残余量 
			minflow = min(minflow, dp[pre[i]][i]);
		for(int i = end;i != start;i = pre[i])	{
			dp[pre[i]][i] -= minflow;			//更新增广路中正反向弧的流量 
			dp[i][pre[i]] += minflow;
		}
		maxflow+=minflow;
	}
}

int main(){
	int n, m;
	while(~scanf("%d%d", &n, &m)){
		int i, a, b, f;
		memset(dp, 0, sizeof(dp));
		for(i = 0;i < n;i++){
			scanf("%d%d%d", &a, &b, &f);
			dp[a][b] += f;
		}
		maxflow = 0;
		Edmonds_Karp(1, m, m);
		printf("%d\n", maxflow);
	}
	return 0;
}



  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值