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

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

上一篇中介绍了网络流的基础,最大流最小割定理的证明,下面来看如何求一个容量网络的最大流,这里介绍四种算法: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];
	}
}

发布了72 篇原创文章 · 获赞 33 · 访问量 5万+

猜你喜欢

转载自blog.csdn.net/Elvirangel/article/details/103745547