LA 5905 Pool construction 最大流

传送门:LA 5905 Pool construction

题目大意:给你一片N * M的网格型地图,其中有的地方是坑,有的地方是草地,将草地变成坑需要花费D,将坑变为草地需要花费F,将坑的边界包围起来需要每单位花费B(因为以后要将坑灌水变成水池,为了防止水溢出,需要围起来)。其中坑可以不连续的分布,但是地图的最外围必须是草地(也就是说只能把坑补上)。问要将所有的坑的边界围起来需要花费的最小值是多少。

题目分析:

大致思路是将草地和坑分成两个集合,草地和超级源点建边,容量为D,坑和超级汇点建边,容量为F,所有相邻点之间建边,容量为B,而且对外围的草地和超级源点建边,容量为oo,保证外围草地不会变成坑。

如果有流量产生,则必定是通过草地集合与坑集合之间的相邻边流过。如果草地变成坑的费用小于与草地相邻的坑数乘以B之和,那么草地变成坑,此时该草地相当于被划分到了坑集合中;如果坑变草的费用小于与坑相邻的草地数乘以B之和,那么坑变草地,此时坑相当于被划分到了草地集合中。如果不变,那么继续流就好了。

反正就是这样啦,自己YY一下就好。最后跑一遍最大流就是答案。

代码如下:


#include <stdio.h>
#include <string.h>
#include <algorithm>
#define clear(A, X) memset (A, X, sizeof A)
#define copy(A, B) memcpy (A, B, sizeof A)
using namespace std;

const int maxE = 1000000;
const int maxN = 10005;
const int maxM = 60;
const int maxQ = 1000000;
const int oo = 0x3f3f3f3f;

struct Edge {
	int v, c, n, rc;
} edge[maxE];//边组


int adj[maxN], cntE, cntEE;
int Q[maxQ], head, tail;//队列
int d[maxN], cur[maxN], pre[maxN], num[maxN];
int s, t, nv;//s:源点,t:汇点,nv:编号修改的上限
int n, m;
int go[4][2] = {{1, 0}, {-1, 0}, {0, 1}, {0, -1}};
int cost_of_d, cost_of_f, cost_of_b;
char G[maxM][maxM];

void addedge (int u, int v, int c) {//添加边
	edge[cntE].v = v;
	edge[cntE].c = c;
	edge[cntE].rc = c;
	edge[cntE].n = adj[u];
	adj[u] = cntE++;
	
	edge[cntE].v = u;
	edge[cntE].c = 0;
	edge[cntE].rc = 0;
	edge[cntE].n = adj[v];
	adj[v] = cntE++;
}

void rev_bfs() {
	clear (num, 0);
	clear (d, -1);
	d[t] = 0;
	num[0] = 1;
	head = tail = 0;
	Q[tail++] = t;

	while (head != tail) {
		int u = Q[head++];
		for (int i = adj[u]; ~i; i = edge[i].n) {
			int v = edge[i].v;
			if (~d[v]) continue;
			d[v] = d[u] + 1;
			Q[tail++] = v;
			num[d[v]]++;
		}
	}
}

int ISAP() {
	copy (cur, adj);
	rev_bfs ();
	int flow = 0, u = pre[s] = s, i;

	while (d[s] < nv) {
		if (u == t) {
			int f = oo, neck;
			for (i = s; i != t; i = edge[cur[i]].v) {
				if (f > edge[cur[i]].c){
					f = edge[cur[i]].c;
					neck = i;
				}
			}
			for (i = s; i != t; i = edge[cur[i]].v) {
				edge[cur[i]].c -= f;
				edge[cur[i] ^ 1].c += f;
			}
			flow += f;
			u = neck;
		}
		for (i = cur[u]; ~i; i = edge[i].n) if (d[edge[i].v] + 1 == d[u] && edge[i].c) break;
		if (~i)  {
			cur[u] = i;
			pre[edge[i].v] = u;
			u = edge[i].v;
		}
		else {
			if (0 == (--num[d[u]])) break;
			int mind = nv;
			for (i = adj[u]; ~i; i = edge[i].n) {
				if (edge[i].c && mind > d[edge[i].v]) {
					cur[u] = i;
					mind = d[edge[i].v];
				}
			}
			d[u] = mind + 1;
			num[d[u]]++;
			u = pre[u];
		}
	}
	
	return flow;
}

void init() {//初始化
	clear(adj, -1);
	cntE = 0;
}

void work () {
	int ans = 0;
	init();
	scanf ("%d%d", &m, &n);
	scanf ("%d%d%d", &cost_of_d, &cost_of_f, &cost_of_b);
	s = n * m; t = s + 1; nv = t + 1;
	for (int i = 0; i < n; ++ i) {
		scanf ("%s", G[i]);
		for (int j = 0; j < m; ++ j) {
			if (!i || n - 1 == i || !j || m - 1 == j) {
				if (G[i][j] == '.') {
					ans += cost_of_f;
					G[i][j] = '#';
				}
				addedge (s, i * m + j, oo);//防止边界草地被改变为坑
			}
			if (G[i][j] == '.') addedge (i * m + j, t, cost_of_f);
			else addedge (s, i * m + j, cost_of_d);
		}
	}
	for (int i = 0; i < n; ++ i)
		for (int j = 0; j < m; ++ j) {
			for(int ks = 0; ks < 4; ++ ks) {
				int x = i + go[ks][0];
				int y = j + go[ks][1];
				if (~x && x < n && ~y && y < m) addedge (i * m + j, x * m + y, cost_of_b);
			}
		}
	printf ("%d\n", ISAP () + ans);
}

int main() {
	int T;
	for (scanf("%d", &T); T; --T) work();
	return 0;
}


你可能感兴趣的:(la)