网络流:初学菜鸟级选手

一.网络流

菜鸟本鸟是第一次接触网络流,觉得新奇又有趣 ,先来介绍一下网络流的定义

接着说一下什么叫可行流。可行流满足两个性质:

  • 容量限制条件:fij<=Cij,那就是你水管上的流量不能超过水管的容量,否则就爆炸了
  • 平衡条件:即从一个点流入的等于从这个点流出的
二.最大流问题

简单点来说就是研究一下如何使你家的水管不爆,最终又能获得最大的水流量的问题,首先介绍一下EK算法。EK算法利用了BFS的思想,先来看一下下面的例子。

先看图一,每条边的容量已经标注在图上了。源点是1,汇点是4。这一眼就能看出来获得最大流的路径是1-2-4和1-3-4。那玩意计算机在bfs遍历时第一次遍历的就是图二的1-2-3-4的路径,那就没了啊,最大流是1。所以我们要想办法看怎么着才能反悔。这时候反向边就登场了(后悔药)。当我们找到一条从源点通向汇点的路径时,一定要将这条路径上所有边的容量减去可以获得的流量c(也就是这条路径上所有边中的最小容量的值). 然后反向边就要加上去了!!!看图三,这就是反向边。反向边的权值要加上c。现在对图进行遍历,又发现了一条合法的路径1-3-2-4.现在总行了吧,最大流就是2了。
那反向边要怎么理解呢?这就是给了你后悔药啊。你看看图四,本来第一条路经从2-3有一滴水,但是我现在想让它退回去到2,然后我就能从3到4了啊,皆大欢喜!!!上代码

#include <iostream>
#include <queue>
#include<string.h>
using namespace std;
#define arraysize 201
int maxData = 0x7fffffff;
int capacity[arraysize][arraysize]; //记录残留网络的容量
int flow[arraysize];                //标记从源点到当前节点实际还剩多少流量可用
int pre[arraysize];                 //标记在这条路径上当前节点的前驱,同时标记该节点是否在队列中
int n, m;
queue<int> myqueue;
int BFS(int src, int des)
{
	int i, j;
	while (!myqueue.empty())       //队列清空
		myqueue.pop();
	for (i = 1; i < m + 1; ++i)
	{
		pre[i] = -1;
	}
	pre[src] = 0;
	flow[src] = maxData;
	myqueue.push(src);
	while (!myqueue.empty())
	{
		int index = myqueue.front();
		myqueue.pop();
		if (index == des)            //找到了增广路径
			break;
		for (i = 1; i < m + 1; ++i)
		{
			if (i != src && capacity[index][i] > 0 && pre[i] == -1)
			{
				pre[i] = index; //记录前驱
				flow[i] = min(capacity[index][i], flow[index]);   //关键:迭代的找到增量
				myqueue.push(i);
			}
		}
	}
	if (pre[des] == -1)      //残留图中不再存在增广路径
		return -1;
	else
		return flow[des];
}
int maxFlow(int src, int des)
{
	int increasement = 0;
	int sumflow = 0;
	while ((increasement = BFS(src, des)) != -1)
	{
		int k = des;          //利用前驱寻找路径
		while (k != src)
		{
			int last = pre[k];
			capacity[last][k] -= increasement; //改变正向边的容量
			capacity[k][last] += increasement; //改变反向边的容量
			k = last;
		}
		sumflow += increasement;
	}
	return sumflow;
}
int main()
{
	int i, j;
	int start, end, ci;
	while (cin >> n >> m)
	{
		memset(capacity, 0, sizeof(capacity));
		memset(flow, 0, sizeof(flow));
		for (i = 0; i < n; ++i)
		{
			cin >> start >> end >> ci;
			if (start == end)               //考虑起点终点相同的情况
				continue;
			capacity[start][end] += ci;     //此处注意可能出现多条同一起点终点的情况
		}
		cout << maxFlow(1, m) << endl;
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值