图论(八)——网络流的最大流问题之Edmonds-Karp算法

最大流

图论(五)——网络流的最大流问题引入

增广路

为了解决最大流问题,再引入一个概念:剩余容量【c(x , y)-f(x , y)】


如果可以找到一条从源点出发,经过中间节点,流向汇点,且其中每一条边的剩余容量均大于0的路,就称这条路为一条增广路,假设可以知道这一条增广路中所有的边的最小剩余容量为p,那么对于整个网络系统而言,就可以增加一个流量为w的流从源点通过这一条路流向汇点,那么这个网络的流量就增加了w。

如果找到了所有的增广路,已经没有其他的增广路可以找时,就说明网络的流量达到了最大,此时的流量就是网络的最大流了


Edmonds-Karp增广路算法

Edmonds-Karp算法,简称为EK算法,就是不断的用BFS(显然这种问题中用BFS比用DFS快捷的多)找增广路,找到所有的增广路的时候就计算出网络的最大流了


当然,在这个过程中还要注意:除了考虑原图的所有边之外,还应该考虑所有边的反向边,这些边可能有些因为没有在图中输入,他们的原始容量为0,特别容易被忽略
这个意思就是:对于一条边(x , y)而言,如果在加入增广路的过程中流过了一个”流“,其剩余流量变成了c(x , y)-f(x , y),那么其反向边的流量也会发生变化,变成了c(y , x)-f(y , x)


图解:
增广路图解
【画的有点丑不要怪我QAQ】


代码实现:(因为还有反向边的储存,所以可以用到邻接表的”成对储存技巧“)

#include<queue>
#include<algorithm>
#include<cstdio>
#include<cstring>
using namespace std;
const int INF = 0x3f3f3f3f;
const int len_node = 2010;
const int len_edge = 20010;  //这个是要开的数组的大小,根据题目自己定就行了

int num_node,num_edge,s,t,cnt,maxflow;
int to[len_edge],edge[len_edge],head[len_node],nxt[len_edge];  //边  
int vis[len_node],minf[len_node],pre[len_node];
/*
minf:  记录每一条增广路上的最小容量
pre:  记录前驱(使得在这条增广路上每一条边由终点可以知道起点) 
*/

void init(){
	memset(head,0,sizeof(head));
	
	s=1;
	t=num_node;
	cnt=1;
	maxflow=0;
}

void addEdge(int u,int v,int w){
	//有向图 存边操作
	to[cnt]=v;
	edge[cnt]=w;
	nxt[cnt]=head[u];
	head[u]=cnt++;
	
	//成对储存 
	to[cnt]=u;
	edge[cnt]=0;
	nxt[cnt]=head[v];
	head[v]=cnt++; 
}   //因为规定了 一对边不能同时为正 所以一条边为正 另一条边就为0 

int x,y; 
bool BFS(){
	memset(vis,0,sizeof(vis));
	
	queue<int>q;
	q.push(s);
	vis[s]=1;
	minf[s]=INF;  //这个用来逐步记录这条增广路上的最小容量 
	
	while(!q.empty()){
		x=q.front();
		q.pop();
		
		for(int i=head[x];i;i=nxt[i]){
			if(edge[i]){
				y=to[i];
				if(vis[y]){
					continue;
				}
				
				minf[y]=min(minf[x],edge[i]);  //更新这个最小容量   这个是从一开始就开始更新的  所以经过这个过程之后  minf[t] 此时就是这一条增广路的最小容量 
				pre[y]=i;  //记录前驱  便于找到最长路的实际方案  在update函数中发挥作用
				q.push(y);
				vis[y]=1;
				if(y==t){
					return 1;  //找到了一条增广路了  直接返回 进行 update() 
				} 
			}
		}
	}
	
	return 0; 
} 

int p,pre_p;
void update(){  //更新增广路及其反向边的剩余容量 
	p=t;
	while(pre_p!=s){
		pre_p=pre[x];
		edge[pre_p] -= minf[t];
		edge[pre_p^1] += minf[t];
		
		p=to[pre_p^1];
	}
	
	maxflow+=minf[t];
}

int main(){
	int a,b,c;
	while(~scanf("%d%d",&num_node,&num_edge)){
		init();
		
		for(int i=1;i<=num_edge;i++){
			scanf("%d%d%d",&a,&b,&c);
			addEdge(a,b,c);
		}
		
		while( BFS() ){
			update();
		}
		
		printf("%d\n",maxflow);
	}
	
	return 0;
}

时间复杂度:O(numNode*numEdge^2)

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值