传送门: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; }