求解最大流的四种算法介绍、利用最大流模型解题入门

上一篇中介绍了网络流的基础,最大流最小割定理的证明,下面来看如何求一个容量网络的最大流,这里介绍四种算法:EK算法、SAP算法、DINIC算法、HLPP算法。这四种算法中,前三种基于增广路,最后一种基于预流推进。

基于增广路的算法

Ford-Fulkerson算法
先来简单提一下Ford-Fulkerson算法。
在上一节中证明了,如果一个可行流中没有增广路,那么此时这个可行流的流量就是最大流,因此Ford-Fulkerson算法就是在一个可行流中不断地遍历寻找增广路,如果有增广路,那么就在这个增广路上做调整(前向弧流量增加,后向弧流量减少)来消除增广路,当在整个可行流中再也没法找到一条增广路时,就得到了最大流。这一思想非常简单,也很好理解,但问题的关键是:如何在一个可行流中高效地寻找增广路?Ford-Fulkerson用的是一种标号法,但是那种方式的时间复杂度可能会依赖于网络流中各边的容量,在最坏的情况下复杂度是O(Ef)(E为边数,f为所有边流量的最大值),例如下图中,如果在查找增广路时先选择A->B->C->D,再选择A->C->B->D,再走A->B->C->D……如此就需要走2000次,而实际上直接走A->B->D,A->C->D两次就完成了。而Furd-Fulkerson算法确实有可能会如前一种的方式进行,因此就不具体介绍了。
EK、SAP、DINIC算法都基于这样的消除增广路的思想,但它们给出了更好的查找增广路的方式,下面来分别来看这三种算法。
在这里插入图片描述

EK算法
最简单的算法莫过于暴力搜索,而EK算法正是如此。
在每次搜索增广路的时候,都采取BFS的策略,将所有的从源点到汇点的路径都找出来,那么如果有增广路,就一定可以将它找出来。因此采用BFS策略首先是正确的,来看一下它的代码实现:

//capacity:容量
//flow:流量
//parent:记录在一条增广路中每个节点的前一个节点
//alpha:记录在增广路中当每个节点所能调整的流量的最大值

int EK(int m)
{
	//初始化操作
	int result = 0;
	for (int i = 1; i <= m; i++)	parent[i] = alpha[i] = 0;
	queue<int> vertexQueue;
	while (true)
	{
		memset(alpha, 0, sizeof(alpha));
		alpha[1] = INF;
		vertexQueue.push(1);
		//BFS过程
		while (!vertexQueue.empty())
		{
			int vtop = vertexQueue.front();
			vertexQueue.pop();
			for (int i = 1 ;  i <= m ; i ++ )
			{
				//如果目标节点还未在增广路中出现并且可以调整流量
				if (!alpha[i] && flow[vtop][i] < capacity[vtop][i])
				{
					parent[i] = vtop;
					alpha[i] = min(capacity[vtop][i] - flow[vtop][i], alpha[vtop]);
					vertexQueue.push(i);
				}
			}
		}
		//汇点可调整流量为0,说明没有增广路了,算法结束
		if (alpha[m] == 0)
		{
			return result;
		}
		//汇点可调整流量不为0,那么找到了增广路,增广路上所有节点做流量调整
		for (int i = m; i != 1; i = parent[i])
		{
			flow[parent[i]][i] += alpha[m];//前向弧流量增加
			flow[i][parent[i]] -= alpha[m];//后向弧流量减少
		}
		//由于一开始流量都为0,调整多少能量就代表整个可行流的流量增加了多少
		result += alpha[m];
	}
}

那么如何评估它的性能呢?首先可以确定的是,它不会出现像Ford-Fulkerson那样的问题,考虑上面那张图所示的情形,如果采用EK算法,是肯定不会走A->B->C->D的,为什么?因为采用BFS获取的路径一定是最短距离的路径,很明显上图中从源点到汇点的最短距离为3,因此EK算法能够避免Ford-Fulkerson遇到的问题。
但是仅仅如此还是不能对EK算法的性能有一个清晰的认识,需要知道其准确的时间复杂度。不妨先来看一个EK算法的运行实例:(图来自wiki)
在这里插入图片描述
红色的路径就是每次BFS所找到的增广路。从这张图中可以观察到一个事实:在每次BFS查找增广路之后,最短增广路的长度一定是非减的,也即对于每一个节点,它到源点的最短距离是非减的。这个性质可以有严格的证明,见《算法导论》(引理26.7 P426),直观上想象一下,如果对于一个给定的图确定了从源点到某一点的最短距离为 d d d,现在在这个图中去掉一些边,那么从源点到这一点要么变得不连通,要么距离会不变,要么距离会增大,绝对不可能减少。因为如果它减少为 d ′ &lt; d d&#x27;&lt;d d<d,那么把去掉的这些边加回来,得到原图,这一过程并没有影响到 d ′ d&#x27; d的这条路径,因此原图中还肯定存在距离为 d ′ d&#x27; d的路径,这与 d d d为最短距离矛盾。增广路调整的过程,就相当于在原图中去掉了一些边,因为某些前向弧变成了满流,后向弧变成了零流,没办法再经过这些边了。
基于这一点,可以证明一个引理:
EK算法中所能找到的增广路的数量为O(VE)
证明
每次调整增广路的时候,所调整的流量为所有边可调整流量的最小值,那么就定义具有最小值的那条边为 关键边,显然每条增广路都必须至少有一条关键边
设流f的源点为s,汇点为t,假设边(u,v)成为某次BFS搜索得到的增广路中的关键边,此时 d i s t ( s , v ) = d i s t ( s , u ) + 1 dist(s,v) = dist(s,u) + 1 dist(s,v)=dist(s,u)+1,在这次增广路流量调整后,这条边的可调整流量将变为0,也就是说(u,v)会从残存网络中消失。如果边(u,v)想要再度成为关键边,那么(u,v)的流量必须要减少,也就是说当(u,v)再度成为关键边时一定有 d i s t ′ ( s , u ) = d i s t ′ ( s , v ) + 1 dist&#x27;(s,u) = dist&#x27;(s,v)+1 dist(s,u)=dist

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值