POJ 1273——Drainage Ditches【最大流 & EK算法 & Dinic算法 + 优化】

题目传送门


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


题意

现在有m个池塘(从1到m开始编号,1为源点,m为汇点),及n条有向水渠,给出这n条水渠所连接的点和所能流过的最大流量,求从源点到汇点能流过的最大流量。


题解

  • 裸最大流
  • 注意题目有重边数据

AC-Code

  • Edmonds-Karp 最短路增广算法
    #include <bits/stdc++.h>
    
    using namespace std;
    typedef long long ll;
    #define ios ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
    
    const int inf = 0x3f3f3f3f;
    int S, T;
    int n, m;
    
    int pre[205];
    int flow[205];
    int c[205][205];
    int BFS() {
    	memset(pre, -1, sizeof pre);
    	pre[S] = 0;
    	flow[S] = inf;
    	queue<int>que;
    	que.push(S);
    	while (!que.empty()) {
    		int op = que.front();
    		que.pop();
    		for (int i = 1; i <= n; i++) {
    			if (i == S || pre[i] != -1 || c[op][i] == 0)	continue;
    			pre[i] = op; //找到未遍历过的点
    			flow[i] = min(flow[op], c[op][i]); // 更新路径上的最小值 
    			que.push(i);
    		}
    	}
    	if (pre[T] == -1)	return -1;
    	return flow[T];
    }
    int EK(int s, int t) {
    	S = s;
    	T = t;
    	int ans = 0;
    	while (1) {
    		int k = BFS();
    		if (k == -1)	break;
    		ans += k;
    		int nw = T;
    		while (nw != S) {	//更新残量网络 
    			c[pre[nw]][nw] -= k;
    			c[nw][pre[nw]] += k;
    			nw = pre[nw];
    		}
    	}
    	return ans;
    }
    int main() {
    	while (cin >> m >> n) {
    		memset(c, 0, sizeof c);
    		for (int i = 0; i < m; i++) {
    			int x, y, z;
    			cin >> x >> y >> z;
    			c[x][y] += z;	// 这里要 += ,不然会WA;应该是有重边数据
    		}
    		cout << EK(1, n) << endl;
    	}
    }
    
  • Dinic 算法
    #include <bits/stdc++.h>
    
    using namespace std;
    typedef long long ll;
    #define ios ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
    
    const int inf = 0x3f3f3f3f;
    int n, m;
    
    struct Edge {
    	int to;
    	int next;
    	int val;
    }edge[205 << 1]; // 双向边,开 2 倍数组
    int head[205];
    int cnt; // 边的数量,从 0 开始编号
    int depth[205]; // 分层图标记深度
    
    void add(int u, int v, int w) {
    	edge[cnt].to = v;
    	edge[cnt].next = head[u];
    	edge[cnt].val = w;
    	head[u] = cnt++;
    }
    
    // bfs分层图
    bool bfs(int s, int t) { 
    	queue<int>q;
    	memset(depth, 0, sizeof depth);
    	depth[s] = 1; // 源点深度为 1
    	q.push(s);
    	while (!q.empty()) {
    		int u = q.front();
    		q.pop();
    		for (int i = head[u]; ~i; i = edge[i].next) {
    			int v = edge[i].to;
    			if (edge[i].val > 0 && depth[v] == 0) { // 如果残量不为0,且 v 还未分配深度
    				depth[v] = depth[u] + 1;
    				q.push(v);
    			}
    		}
    	}
    	return depth[t]; // 汇点深度为 0:不存在分层图,返回false;
    					 //           非 0 :存在增广路,返回true
    }
    
    // dfs寻找增广路
    int dfs(int u, int flow, int t) { 
    	if (u == t || flow <= 0) // 到达汇点
    		return flow;
    	int rest = flow;
    	for (int i = head[u]; ~i; i = edge[i].next) {
    		int v = edge[i].to;
    		if (depth[v] == depth[u] + 1 && edge[i].val != 0) { // 满足分层图、残量>0 两个条件
    			int k = dfs(v, min(rest, edge[i].val), t); // 向下增广
    			rest -= k;
    			edge[i].val -= k; // 正向边减
    			edge[i ^ 1].val += k; // 反向边加
    		}
    	}
    	return flow - rest; // flow:推送量,rest:淤积量,flow - rest:接受量/成功传递量
    }
    
    int Dinic(int s, int t) {
    	int ans = 0;
    	while (bfs(s, t)) {
    		while (int d = dfs(s, inf, t))
    			ans += d;
    	}
    	return ans;
    }
    
    int main() {
    	while (cin >> m >> n) {
    		cnt = 0;
    		memset(head, -1, sizeof head);
    		for (int i = 1; i <= m; i++) {
    			int x, y, z;
    			cin >> x >> y >> z;
    			add(x, y, z);
    			add(y, x, 0);
    		}
    		cout << Dinic(1, n) << endl;
    	}
    }
    
  • Dinic 算法 + 当前弧优化 + 无用点优化
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define ios ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);

const int inf = 0x3f3f3f3f;
int n, m;

struct Edge {
	int to;
	int next;
	int val;
}edge[205 << 1]; // 双向边,开 2 倍数组
int head[205];
int cnt; // 边的数量,从 0 开始编号
int depth[205]; // 分层图标记深度
int cur[205]; // 当前弧优化,记录当前点 u 循环到了哪一条边
void add(int u, int v, int w) {
	edge[cnt].to = v;
	edge[cnt].next = head[u];
	edge[cnt].val = w;
	head[u] = cnt++;
}

// bfs分层图
bool bfs(int s, int t) { 
	queue<int>q;
	memset(depth, 0, sizeof depth);
	depth[s] = 1; // 源点深度为 1
	cur[s] = head[s];
	q.push(s);
	while (!q.empty()) {
		int u = q.front();
		q.pop();
		for (int i = head[u]; ~i; i = edge[i].next) {
			int v = edge[i].to;
			if (edge[i].val > 0 && depth[v] == 0) { // 如果残量不为0,且 v 还未分配深度
				depth[v] = depth[u] + 1;
				cur[v] = head[v]; //------当前弧优化,注意在bfs里,这样做到了“按需赋值”,因为Dinic本来上界就松得一匹, BFS的过程中不连通的点根本就不用再管了...
				if (v == t) // -----分层图汇点优化:遇到汇点直接返回------
					return true;
				q.push(v);
			}
		}
	}
	return depth[t]; // 汇点深度为 0:不存在分层图,返回false;
					 //           非 0 :存在增广路,返回true
}

// dfs寻找增广路
int dfs(int u, int flow, int t) { 
	if (u == t || flow <= 0) // 到达汇点
		return flow;
	int rest = flow;
	for (int i = cur[u]; ~i; i = edge[i].next) {
		int v = edge[i].to;
		if (depth[v] == depth[u] + 1 && edge[i].val != 0) { // 满足分层图、残量>0 两个条件
			int k = dfs(v, min(rest, edge[i].val), t); // 向下增广
			if (k < 0) // ------无用点优化-----
				depth[v] = 0;
			rest -= k;
			edge[i].val -= k; // 正向边减
			edge[i ^ 1].val += k; // 反向边加
			if (rest <= 0) //------剩余量优化:在进行增广的时候,如果该节点已经没有流量,直接退出------
				break;
		}
	}
	return flow - rest; // flow:推送量,rest:淤积量,flow - rest:接受量/成功传递量
}
int Dinic(int s, int t) {
	int ans = 0;
	while (bfs(s, t)) {
		while (int d = dfs(s, inf, t))
			ans += d;
	}
	return ans;
}
int main() {
	while (cin >> m >> n) {
		cnt = 0;
		memset(head, -1, sizeof head);
		for (int i = 1; i <= m; i++) {
			int x, y, z;
			cin >> x >> y >> z;
			add(x, y, z);
			add(y, x, 0);
		}
		cout << Dinic(1, n) << endl;
	}
}
发布了104 篇原创文章 · 获赞 60 · 访问量 5848

猜你喜欢

转载自blog.csdn.net/Q_1849805767/article/details/103596497