传送门
一个 n×m 的网格,有 K 个障碍格子,其余格子均可以放一个士兵。
求使得第 i 行士兵数量大于等于 Li,第 j 列士兵数量大于等于 Ci 时的最小士兵放置数量。
n,m≤100,K≤n×m。
这题有一个比较显然的上下界网络流解法:
对每一行 i,从源点 S 向点 i 连一条下界为 Li 的边;
对每一列 j,从 n+j 向汇点 T 连一条下界为 Ci 的边;
对每一行 i 以及每一列 j, 如果格子 (i,j) 不是障碍格子,则从 i 向 j 连一条上界为 1 的边,表示在格子 (i,j) 上放置一个士兵。
建完图后跑一个有源汇的上下界网络流即可。
但是我们会发现,这张图从 S 连向 1∼n 以及从 n+1∼n+m 连向 T 的这些边都只有下界没有上界,那么我们是否可以对模型做一些改变,把下界改为上界,就可以使用普通最大流算法来解决这个问题了。
我们从逆向考虑这个问题。先假设一开始在所有的非障碍格子都放了一个士兵,然后接下来我们要计算的就是:在满足限制的情况下,最多能够删掉多少个士兵。
我们设 totLi 表示第 i 行的非障碍格子数量,totCj 表示第 j 列的非障碍格子数量。
对于每一行 i,从源点 S 向点 i 连一条容量为 totLi−Li 的边;
对于每一行 j,从 n+j 向汇点 T 连一条容量为 totCi−Ci 的边;
对于每一行 i 和每一行 j,从 i 向 n+j 连一条容量为 1 的边。
在这张图上跑最大流算法,最终的答案就是 n×m−K−max_flow。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
|
#include <cstdio>
#include <cstring>
#include <iostream>
#include <queue>
using namespace std;
const int _ = 2e2 + 7;
const int __ = 2e4 + 7;
const int inf = 1e9;
const int S = 2e2 + 1, T = 2e2 + 2;
int n, m, K, L[_], C[_], totL[_], totC[_], maxflow;
bool b[_][_];
int dis[_], lst[_], cur[_], nxt[__], to[__], cap[__], tot = 1;
queue<int> q;
void Add(int x, int y, int c) {
nxt[++tot] = lst[x], to[tot] = y, cap[tot] = c, lst[x] = tot;
nxt[++tot] = lst[y], to[tot] = x, cap[tot] = 0, lst[y] = tot;
}
bool Bfs() {
memset(dis, 0, sizeof dis);
while (!q.empty()) q.pop();
dis[S] = 1, q.push(S);
while (!q.empty()) {
int u = q.front(); q.pop();
for (int i = lst[u]; i; i = nxt[i]) {
int v = to[i];
if (dis[v] or !cap[i]) continue;
dis[v] = dis[u] + 1, q.push(v);
if (v == T) return 1;
}
}
return 0;
}
int Extend(int u, int flow) {
if (u == T) return flow;
int res = flow;
for (int &i = cur[u]; i; i = nxt[i]) {
int v = to[i];
if (dis[v] != dis[u] + 1 or !cap[i]) continue;
int cst = Extend(v, min(cap[i], res));
cap[i] -= cst, cap[i ^ 1] += cst, res -= cst;
if (!res) break;
}
return flow - res;
}
void Dinic() {
int flow;
while (Bfs()) {
memcpy(cur, lst, sizeof cur);
do {
flow = Extend(S, inf);
maxflow += flow;
} while (flow);
}
}
int main() {
cin >> n >> m >> K;
for (int i = 1; i <= n; ++i) cin >> L[i], totL[i] = m;
for (int i = 1; i <= m; ++i) cin >> C[i], totC[i] = n;
for (int i = 1, x, y; i <= K; ++i) {
cin >> x >> y;
if (!b[x][y]) b[x][y] = 1, --totL[x], --totC[y];
}
for (int i = 1; i <= n; ++i) {
if (totL[i] < L[i]) { puts("JIONG!"); exit(0); }
Add(S, i, totL[i] - L[i]);
}
for (int i = 1; i <= m; ++i) {
if (totC[i] < C[i]) { puts("JIONG!"); exit(0); }
Add(n + i, T, totC[i] - C[i]);
}
for (int i = 1; i <= n; ++i)
for (int j = 1; j <= m; ++j)
if (!b[i][j]) Add(i, n + j, 1);
Dinic();
cout << n * m - K - maxflow << endl;
return 0;
}
|
题解 P4311 【士兵占领】 by GGN_2015。