网络流

网络流算法以及最小费用最大流算法

想必能看到这篇的人基本都对网络流有一定了解,但是为了照顾所有人我将先对这个概念进行解释

什么是网络流问题?

简单来说就是水流从一个源点s通过很多路径,经过很多点,到达汇点t,问你最多能有多少水能够到达t点
在这里插入图片描述

问题求解思路

我们看到这个问题首先不必急于求解,我们先进行问题分析,首先因为有多条路可以到达t点所以每条路都有一个流量,那么有人肯定会说那就每条流量最大不就行了吗,但是要意识到每个边都有一个权值,便是此条管道最大流量,不能超过否则会撑爆管道,所以我们要对所有可行路进行搜索,将会得到不同的路径序列,将每个序列中的最小值作为此路径上的流;

比如:s->4->5->t我们可以发现最小值为45,所以我们得到此路径最大流45

我们依照此方式进行广度优先搜索将会得到不同序列的不同最大流,然后定义全局变量sum每次加上每条路径最大流即可,看似没有问题,可是让我们回顾下我们的算法有什么不足呢,首先我们搜索方式是无序的,那么有可能找到的是次优解,所以我们应该保证每种情况下的结果都考虑到,当然有些人肯定会想到回溯算法,但是回溯虽然好用但是数据量一大就会爆,所以我们应该跟换一种方式,我们可以再每次搜到一条路径就对每个子路径减去最大流,同时加上反向边(可以用旅行问题来理解这个方式)反向边初始为零每次正向边减去多少反向边就加上多少,搜索直到无增广路为止。

然后后续操作就跟我们之前想的一样了,每次进行广度优先搜索,然后sum变量加上每次得到的最大流。

这里主要给出关键部分代码

int pre[1000]
int inque[1000]
bool bfs()
{
	queue<int> q;
	memset(inque,0,sizeof(inque));
	memset(pre,-1,sizeof(pre))
	inque[begin]=1;
	q.push(begin);
	while(!q.empty())
	{
		int u=q.front;
		q.pop;
		for(int i=head[u];i;i=node[i].next)
		{
			int k=node[i].v;
			if(!inque[k]&&node[i].val)
			{
				inque[k]=1;
				pre[d].v=u;
				pre[k].edge=i;
				if(k==t)return 1;
				q.push(k);
			}
		}
	}
	return 0;
}

int EK()
{
	int sum;
	while(bfs())
	{
		int l=100000;
		for(int i=t;i!=s;i=pre[i].v)
		{
			l=min(l,node[pre[i].edge].val);
		}
		for(int i=t;i!=s;i=pre[i].v)
		{
			node[pre[i].edge].val-=l;
			node[pre[i].edge^1].val+=l;
		}
		sum+=l;
	}
	return ans;
}

以上就是代码部分很好理解吧,加下来我们来思考,如果每个边都有一个权值作为单位流量的费用,我们要求最小花费情况下的最大流,应该怎么求。

首先我们应该知道这是一个求最短路径的问题模型,但是这个问题中因为我们仍然采用寻找增广路模式,如果我们增加回退边的话,势必要引入负权边,那么诸如迪杰斯特拉,floyd算法便没有了用武之地,但是前辈已经为我们想出了一个好办法我们可以用SPFA算法解决这个问题,其实SPFA算法实际上就是基于队列实现的,下面是具体算法

【队列+松弛操作】

读取队头顶点u,并将队头顶点u出队(记得消除标记);将与点u相连的所有点v进行松弛操作,如果能更新估计值(即令d[v]变小),那么就更新,另外,如果点v没有在队列中,那么要将点v入队(记得标记),如果已经在队列中了,那么就不用入队,这样不断从队列中取出顶点来进行松弛操作。

以此循环,直到队空为止就完成了单源最短路的求解。

【算法过程】

设立一个队列用来保存待优化的顶点,优化时每次取出队首顶点 u,并且用 u 点当前的最短路径估计值dis[u]对与 u 点邻接的顶点 v 进行松弛操作,如果 v 点的最短路径估计值dis[v]可以更小,且 v 点不在当前的队列中,就将 v 点放入队尾。这样不断从队列中取出顶点来进行松弛操作,直至队列空为止。

【检测负权回路】

方法:如果某个点进入队列的次数大于等于 n,则存在负权回路,其中 n 为图的顶点数

然后我们只需要对上述代码进行小幅度改动即可,将搜索部分改为SPFA即可。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值