POJ 1273 Drainage Ditches (网络流 EK && Dinic)

本文介绍了一个经典的网络流问题——排水沟渠问题,该问题通过构建排水系统来解决农场积水问题。文章提供了两种解决方案:EK算法和Dinic算法,并附带了完整的代码实现。
Drainage Ditches
Time Limit: 1000MS Memory Limit: 10000K
Total Submissions: 53153 Accepted: 20238

Description

Every time it rains on Farmer John's fields, a pond forms over Bessie's favorite clover patch. This means that the clover is covered by water for awhile and takes quite a long time to regrow. Thus, Farmer John has built a set of drainage ditches so that Bessie's clover patch is never covered in water. Instead, the water is drained to a nearby stream. Being an ace engineer, Farmer John has also installed regulators at the beginning of each ditch, so he can control at what rate water flows into that ditch. 
Farmer John knows not only how many gallons of water each ditch can transport per minute but also the exact layout of the ditches, which feed out of the pond and into each other and stream in a potentially complex network. 
Given all this information, determine the maximum rate at which water can be transported out of the pond and into the stream. For any given ditch, water flows in only one direction, but there might be a way that water can flow in a circle. 

Input

The input includes several cases. For each case, the first line contains two space-separated integers, N (0 <= N <= 200) and M (2 <= M <= 200). N is the number of ditches that Farmer John has dug. M is the number of intersections points for those ditches. Intersection 1 is the pond. Intersection point M is the stream. Each of the following N lines contains three integers, Si, Ei, and Ci. Si and Ei (1 <= Si, Ei <= M) designate the intersections between which this ditch flows. Water will flow through this ditch from Si to Ei. Ci (0 <= Ci <= 10,000,000) is the maximum rate at which water will flow through the ditch.

Output

For each case, output a single integer, the maximum rate at which water may emptied from the pond.

Sample Input

5 4
1 2 40
1 4 20
2 4 20
2 3 30
3 4 10

Sample Output

50


1、EK

思路:

       网络流EK算法入门模板题;

       资料 http://blog.sina.com.cn/s/blog_6cf509db0100uy5n.html


代码:

#include <stdio.h>
#include <string.h>
#include <queue>
#define INF 0xfffffff
#define N 220

using namespace std;

int map[N][N];
bool vis[N];			// bfs 时标记,避免走回路
int pre[N];				// 父节点

bool bfs(int start, int num)
{
	memset(pre, -1, sizeof(pre));		// 父节点初始化成-1
	memset(vis, 0, sizeof(vis));
	queue<int>q;

	pre[start] = start;					// 起点的父节点是本身
	vis[start] = 1;
	q.push(start);

	int cur;
	while(!q.empty()){
		cur = q.front();
		q.pop();

		for(int i = 1; i <= num; i ++){
			if(map[cur][i] > 0 && !vis[i]){		// 有关系,并且未曾访问
				pre[i] = cur;
				vis[i] = 1;
				if(i == num)					// 到达终点
					return 1;
				q.push(i);
			}
		}
	}

	return 0;
}

int EK(int start, int num)
{
	int ans = 0, min_w;
	while( bfs(start, num) ){
		min_w = INF;
		for(int i = num; i != start; i = pre[i]){				// 寻找最小边权,(逆向,找父节点)
			min_w = min_w < map[pre[i]][i] ? min_w : map[pre[i]][i];
		}

		for(int i = num; i != start; i = pre[i]){				// 更新边权,建立反向边
			map[pre[i]][i] -= min_w;
			map[i][pre[i]] += min_w;
		}
		ans += min_w;
	}

	return ans;
}

int main()
{
	int n, m;
	int start, end, p, ans;
	while(scanf("%d%d", &m, &n) != EOF){
		memset(map, 0, sizeof(map));

		for(int i = 0; i < m; i ++){
			scanf("%d%d%d", &start, &end, &p);
			map[start][end] += p;				// 注意,可能有重边
		}
		
		ans = EK(1, n);
		printf("%d\n", ans);
	}

	return 0;
}

2、Dinic

思路:

      bfs时每一步对原图进行分层,然后用DFS求增广路;

代码:

#include <stdio.h>
#include <string.h>
#include <queue>
#define INF 0x7fffffff
#define N 220
#define min(a, b) a < b ? a : b

using namespace std;

int level[N];				// 用于分层
int map[N][N];
int start, end;

bool bfs()
{
	queue<int>q;
	q.push(start);
	memset(level, 0, sizeof(level));
	level[start] = 1;
	
	int cur;
	while(!q.empty()){
		cur = q.front();
		q.pop();

		for(int i = 1; i <= end; i ++){
			if(!level[i] && map[cur][i] > 0){		// 条件:之前未被分层,并且两点有边权
				level[i] = level[cur] + 1;
				if(i == end){						// 到达终点
					return 1;
				}
				q.push(i);
			}
		}
	}
	
	return 0;
}

int dfs(int cur, int cp)
{
	if(cur == end)									// 到达终点
		return cp;									// 返回值是整条路径最小边权值

	int t, tmp = cp;
	for(int i = start; i <= end && tmp; i ++){
		if(level[i] == level[cur] + 1 && map[cur][i] > 0){		// 条件:两点之间层数相差1,并且两点之间有边权
			t = dfs(i, min(tmp, map[cur][i]));					// t接收此条路径最小边权值
			map[cur][i] -= t;						// 更新边权
			map[i][cur] += t;						// 建立反向边
			tmp -= t;
		}
	}

	return cp - tmp;
}

int Dinic()
{
	int ans = 0, min_w;
	while( bfs() ){							// 判断能否走通
		while( (min_w = dfs(start, INF)) ){			// 深搜整条路径
			ans += min_w;
		}
	}

	return ans;
}

int main()
{
	start = 1;
	int ct, x, y, p, ans;
	while(scanf("%d%d", &ct, &end) != EOF){
		memset(map, 0, sizeof(map));

		for(int i = 0; i < ct; i ++){
			scanf("%d%d%d", &x, &y, &p);
			map[x][y] += p;
		}

		ans = Dinic();
		printf("%d\n", ans);
	}

	return 0;
}


评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值