[hdu 1532] Drainage Ditches(最大流dinic)

算是一个网络流入门介绍贴吧,这里就不说各种剩余图,层次图的概念了,这些网上太多了,这里直接说些实在点的东西吧。

网络:的意思是给定一个网络(有向图),这个网络中有起点也有终点,各条边上的流量表示最大值,也就是有多少单位的东西能够经过这条边。

最大流是网络流的其中一类问题,求的是从起点出发,最终有多少个"货物"能到达终点。以一个图来理解一下这个最大流:

这个图的答案应该是9,1->2->4->5能运送4个货物,1->3->5能运送5个货物,所以到了终点一共是9个货物。有必要补充几点:

1.一条路上能运送的最大数量取决去这条路上运送能力最弱的路段,即数值最小的边。这句话对稍后的编程非常重要。

2.起点运送到终点,所以可以看成起点是仓库(起点能运出来无穷多的货物),终点也是仓库(起点能够运的过来多少东西,终点就能接收多少东西),唯一受限制的就是每个路段的运送能力了。

回到这个题目,首先先将题目抽象:

        告诉有n条边,共m个点(1~m),然后是n条边的头尾与流量。1是起点,m是终点,问能运达终点的最大流量是多少。

这里简要介绍一下dinic算法:

1.用bfs对各顶点标号,计算出层次图,如果层次图中没有出现终点,那么说明最大流计算完毕。

        层次图的计算方法为:设一个dep数组表示各结点的层次(可以理解成从起点出发的深度)。从已访问过的结点出发,如果从这个点出去的没访问的边的流量大于零,那么这条边的终点那个结点的层次为起点的层次+1.

int bfs() // 建立层次图
{
	queue<int> q;
	memset(dep, -1, sizeof(dep)); // 初始化结点的层次
	dep[st] = 0; // 从起点开始,起点层次为0(深度0)
	q.push(st);
	while(!q.empty())
	{
		int cur = q.front();
		q.pop();
		for(int i = head[cur]; i != -1; i = edge[i].next)
		{
			int s = edge[i].t;
			if(dep[s] == -1 && edge[i].flow > 0) // 未访问过, 且这条边的流量大于零
			{
				dep[s] = dep[cur] + 1;
				q.push(s);
			}
		}
	}
	// 若终点不在层次图中, 说明bfs的时候没访问到终点
	// 也就是说通向终点的边的流量都变成0了
	return dep[end] != -1;
}

2.用dfs找出一条通往终点的可行的路,这条路称为增广路,设这条路上流量最小值为x,那么整条路每个路段流量减去x,答案加上x。

int dfs(int st, int limit) // 起点和限制流量(从起点到当前的边的最小流量)
{
	int x;
	if(st == end) // 当走到终点时,一路下来的最小值就是答案, 所以返回limit
		return limit;
	for(int i = head[st]; i != -1; i = edge[i].next)
	{
		int s = edge[i].t;
		if(edge[i].flow > 0 && dep[s] == dep[st] + 1) // 一路过来, 层次逐渐加深
		{
			x = dfs(s, min(limit, edge[i].flow));
			if(x == 0)continue; // 这条路不通, 找下一条路
			edge[i].flow -= x;
			return x;
		}
	}
	return 0;
}
3.转步骤2继续构建层次图和寻找增广路,直到增广完毕。


分析一下这道题的样例数据,首先构图(起点和终点用绿色以示突出):


第一步是构建层次图(红色为层次):

dfs寻找增广路,找到了:

目前为止知道至少有20的流量能到终点了,然后在原图中减掉这条路的流量,为了方便,如果哪条路流量为0就不再画出。


继续建立层次图:

找增广路:


目前为止已有20+现在的20=40的流量能到终点了,继续更新原图。

建立层次图以及寻找增广路:

20+20+10 = 50

现在的图:

增广完毕,答案为50.

其实还有个反向边的概念,上图的数据中因为没用到所以没有画出来,不过这是不可忽略的。本文已十分冗长,只是起个抛砖引玉的作用,关于反向边的概念请另行搜索网络流神牛们的文章。

附上完整程序:

#include<iostream>
#include<cstdio>
#include<queue>
#include<cstring>
using namespace std;

#define maxn 210
#define inf 0x3f3f3f3
struct Edge
{
	int t;
	int flow;
	int next;
}edge[maxn * 3];

int head[maxn];
int dep[maxn];
int n, m;
int st, end;
int edgenum;

int min(int a, int b)
{
	return a < b ? a : b;
}

void addedge(int s, int t, int flow)
{
	edge[edgenum].t = t;
	edge[edgenum].flow = flow;
	edge[edgenum].next = head[s];
	head[s] = edgenum++;
}

int bfs() // 建立层次图
{
	queue<int> q;
	memset(dep, -1, sizeof(dep)); // 初始化结点的层次
	dep[st] = 0; // 从起点开始,起点层次为0(深度0)
	q.push(st);
	while(!q.empty())
	{
		int cur = q.front();
		q.pop();
		for(int i = head[cur]; i != -1; i = edge[i].next)
		{
			int s = edge[i].t;
			if(dep[s] == -1 && edge[i].flow > 0) // 未访问过, 且这条边的流量大于零
			{
				dep[s] = dep[cur] + 1;
				q.push(s);
			}
		}
	}
	// 若终点不在层次图中, 说明bfs的时候没访问到终点
	// 也就是说通向终点的边的流量都变成0了
	return dep[end] != -1;
}

int dfs(int st, int limit) // 起点和限制流量(从起点到当前的边的最小流量)
{
	int x;
	if(st == end) // 当走到终点时,一路下来的最小值就是答案, 所以返回limit
		return limit;
	for(int i = head[st]; i != -1; i = edge[i].next)
	{
		int s = edge[i].t;
		if(edge[i].flow > 0 && dep[s] == dep[st] + 1) // 一路过来, 层次逐渐加深
		{
			x = dfs(s, min(limit, edge[i].flow));
			if(x == 0)continue; // 这条路不通, 找下一条路
			edge[i].flow -= x;
			edge[i^1].flow += x;
			return x;
		}
	}
	return 0;
}

int dinic()
{
	int flow = 0, t;
	while(bfs())
	{
		flow += dfs(st, inf);
	}
	return flow;
}

int main()
{
	int a, b, c;
	while(~scanf("%d%d", &n, &m))
	{
		edgenum = 0;
		memset(head, -1, sizeof(head));
		for(int i = 0; i < n; i++)
		{
			scanf("%d%d%d", &a, &b, &c);
			addedge(a, b, c);
			addedge(b, a, 0);
		}
		st = 1, end = m;
		int re = dinic();
		printf("%d\n", re);
	}
	return 0;
}



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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值