只要你跑的够快,锅就追不上你

0%

「WF 2012」Chips Challenge(费用流)

题目大意

「WF 2012」Chips Challenge(UVA 1104)

给定一个 $n \times n$ 的棋盘,有些格子已经放上了黑色或白色的棋子。要求你在剩下的格子中摆放棋子,满足条件:

  • 第 $i$ 行的黑子个数等于第 $i$ 列的黑子个数。
  • 每行的黑子个数不大于总黑子个数的 $\frac{A}{B}$。

求最多再放多少黑子。

数据范围:$n \le 50$。

思路分析

枚举每行每列最多放 $k$ 枚黑子,这样第二个限制就变成了总黑子数量不能超过 $\frac{B \cdot k}{A}$。

考虑建图:

  • $S$ 向 $R_i$ 连边,代价为 $0$,流量为这一行最多可能摆放的黑子数量。
  • $C_i$ 向 $T$ 连边,代价为 $0$,流量为这一列最多可能摆放的黑子数量。
  • $R_i$ 向 $C_i$ 连边,代价为 $0$,流量为 $k$。
  • 如果 $(i, j)$ 可以不放零件,那么 $R_i$ 向 $C_j$ 连边,代价为 $1$,流量为 $1$。

图中流量的含义:先走第 $4$ 类边表示放置白子,之后如果有可行解,那么残量网络必定左右对称并且和 $S, T$ 相邻的边的的容量都不大于 $k$,这样就可以给剩下的格子全部放上黑子。

于是我们可以发现,如果网络满流,就形成了一个可行方案。而满流情况下最小的费用就是最少放置白子的数量。

时间复杂度 $O(n \times \text{MCMF}(n, n^2))$。

代码实现

1
#include <cstdio>
2
#include <cstring>
3
#include <queue>
4
using namespace std;
5
6
const int maxn = 50, maxv = 102, maxe = 2 * maxn * (maxn + 3);
7
char s[maxn + 3];
8
int T, n, A, B, a[maxn + 3][maxn + 3], Rc[maxn + 3], Cc[maxn + 3];
9
int tot, ter[maxe + 3], nxt[maxe + 3], len[maxe + 3], wei[maxe + 3], lnk[maxv + 3];
10
11
void add_edge(int u, int v, int w, int l) {
12
	ter[++tot] = v, wei[tot] = w, len[tot] = l;
13
	nxt[tot] = lnk[u], lnk[u] = tot;
14
}
15
16
void add_fedge(int u, int v, int w, int l) {
17
	add_edge(u, v, w, l), add_edge(v, u, 0, -l);
18
}
19
20
inline int adj(int x) {
21
	return x & 1 ? x + 1 : x - 1;
22
}
23
24
namespace mcmf {
25
	int sz, src, snk;
26
	queue<int> Q;
27
	bool in[maxv + 3];
28
	int dist[maxv + 3], lst[maxv + 3], mn[maxv + 3];
29
	void init(int n, int s, int t) {
30
		sz = n, src = s, snk = t;
31
	}
32
	bool solve(int &mc, int &mf) {
33
		memset(dist, 0x3f, sizeof(dist));
34
		memset(lst, 0, sizeof(lst));
35
		memset(mn, 0x3f, sizeof(mn));
36
		dist[src] = 0, in[src] = true, Q.push(src);
37
		while (!Q.empty()) {
38
			int u = Q.front();
39
			in[u] = false, Q.pop();
40
			for (int i = lnk[u], v, w, l; i; i = nxt[i]) {
41
				v = ter[i], w = wei[i], l = len[i];
42
				if (w && dist[u] + l < dist[v]) {
43
					dist[v] = dist[u] + l, lst[v] = i, mn[v] = min(mn[u], w);
44
					if (!in[v]) in[v] = true, Q.push(v);
45
				}
46
			}
47
		}
48
		if (!lst[snk]) return false;
49
		mf = mn[snk], mc = mf * dist[snk];
50
		int x = snk;
51
		while (x != src) {
52
			wei[lst[x]] -= mf;
53
			wei[adj(lst[x])] += mf;
54
			x = ter[adj(lst[x])];
55
		}
56
		return true;
57
	}
58
	void main(int &mc, int &mf) {
59
		mc = mf = 0;
60
		int cc = 0, cf = 0;
61
		while (solve(cc, cf)) {
62
			mc += cc, mf += cf;
63
		}
64
	}
65
}
66
67
int main() {
68
	while (scanf("%d %d %d", &n, &A, &B), n) {
69
		for (int i = 1; i <= n; i++) {
70
			scanf("%s", s + 1);
71
			for (int j = 1; j <= n; j++) {
72
				a[i][j] = s[j] == '.' ? -1 : s[j] == '/' ? 0 : 1; 
73
			}
74
		}
75
		for (int i = 1; i <= n; i++) {
76
			Rc[i] = Cc[i] = 0;
77
		}
78
		int cnt = 0, sum = 0;
79
		for (int i = 1; i <= n; i++) {
80
			for (int j = 1; j <= n; j++) {
81
				if (a[i][j]) {
82
					Rc[i]++, Cc[j]++, sum++;
83
					if (a[i][j] == 1) cnt++;
84
				}
85
			}
86
		}
87
		int s = 2 * n + 1, t = s + 1, ans = -1;
88
		for (int k = 0; k <= n; k++) {
89
			tot = 0;
90
			memset(lnk, 0, sizeof(lnk));
91
			mcmf::init(t, s, t);
92
			for (int i = 1; i <= n; i++) {
93
				add_fedge(s, i, Rc[i], 0);
94
				add_fedge(i, i + n, k, 0);
95
				add_fedge(i + n, t, Cc[i], 0);
96
			}
97
			for (int i = 1; i <= n; i++) {
98
				for (int j = 1; j <= n; j++) {
99
					if (a[i][j] == -1) {
100
						add_fedge(i, j + n, 1, 1);
101
					}
102
				}
103
			}
104
			int mc = 0, mf = 0;
105
			mcmf::main(mc, mf);
106
			if (mf == sum && B * k <= A * (sum - mc)) {
107
				ans = max(ans, sum - cnt - mc);
108
			}
109
		}
110
		printf("Case %d: ", ++T);
111
		if (~ans) printf("%d\n", ans);
112
		else puts("impossible");
113
	}
114
	return 0;
115
}