BZOJ1565【NOI2009】植物大战僵尸 <最大权闭合子图+拓扑>

Problem

【NOI2009】植物大战僵尸

Time Limit: 10Sec10 Sec
Memory Limit: 64MB64 MB

Description

![](http://www.lydsy.com/JudgeOnline/images/1565_1.jpg)

Input

![](http://www.lydsy.com/JudgeOnline/images/1565_2.jpg)

Output

仅包含一个整数,表示可以获得的最大能源收入。注意,你也可以选择不进行任何攻击,这样能源收入为00

Sample Input

1
2
3
4
5
6
7
3 2
10 0
20 0
-10 0
-5 1 0 0
100 1 2 1
100 0

Sample Output

1
25

HINT

在样例中, 植物P1,1P_{1,1}可以攻击位置(0,0)(0,0), P2,0P_{2, 0}可以攻击位置(2,1)(2,1)
一个方案为,首先进攻P1,1,P0,1P_{1,1}, P_{0,1},此时可以攻击P0,0P_{0,0} 。共得到能源收益为(5)+20+10=25(-5)+20+10 = 25。注意, 位置(2,1)(2,1)被植物P2,0P_{2,0}保护,所以无法攻击第22行中的任何植物。
数据规模
20%20\%的数据满足1N,M51\le N, M\le 5
40%40\%的数据满足1N,M101 \le N, M \le10
100%100\%的数据满足1N201\le N\le 201M301\le M\le 30104Score104-10^4\le Score\le 10^4

标签:最大权闭合子图 网络流 拓扑

Solution

可以发现,如果一个植物能被吃,则要先吃掉它右边的植物和所有攻击范围有它的植物。
这样可以形成一个有向图。所得到的最大收入为最大权闭合子图。
发现此有向图是可以有环的,而环上的植物一定是不能被吃的。
因而先拓扑一遍,标记掉环上的点,然后跑最小割即可。

Code

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
#include <bits/stdc++.h>
#define MAX_N 1000
#define MAX_M 1000000
#define INF 0x7f7f7f7f
using namespace std;
struct node {int v, c, nxt;} E[MAX_M+5]; vector <int> G[MAX_N+5]; bool mrk[MAX_N+5];
int n, m, s, t, cnt, sum, c[MAX_N+5], d[MAX_N+5], pr[MAX_N+5], cr[MAX_N+5], into[MAX_N+5];
void init() {s = 0, t = n*m+1, cnt = 0; memset(pr, -1, sizeof pr);}
void insert(int u, int v, int c) {E[cnt].v = v, E[cnt].c = c, E[cnt].nxt = pr[u], pr[u] = cnt++;}
void addedge(int u, int v, int c) {insert(u, v, c), insert(v, u, 0); G[u].push_back(v), into[v]++;}
void topo() {
queue <int> que; mrk[s] = mrk[t] = true;
for (int i = s; i <= t; i++) if (!into[i]) que.push(i), mrk[i] = true;
while (!que.empty()) {
int u = que.front(); que.pop();
if (c[u] > 0) sum += c[u];
for (int i = 0; i < (int)G[u].size(); i++) {
int v = G[u][i]; if (mrk[v]) continue;
if (!--into[v]) que.push(v), mrk[v] = true;
}
}
}
bool BFS() {
queue <int> que; que.push(s);
memset(d, -1, sizeof d), d[s] = 0;
while (!que.empty()) {
int u = que.front(); que.pop();
for (int i = pr[u]; ~i; i = E[i].nxt) {
int v = E[i].v, c = E[i].c;
if (!c || ~d[v] || !mrk[v]) continue;
d[v] = d[u]+1, que.push(v);
}
}
return ~d[t];
}
int DFS(int u, int flow) {
if (u == t) return flow;
int ret = 0;
for (int &i = pr[u]; ~i; i = E[i].nxt) {
int v = E[i].v, c = E[i].c;
if (!c || d[v] != d[u]+1 || !mrk[v]) continue;
int tmp = DFS(v, min(flow, c));
E[i].c -= tmp, E[i^1].c += tmp;
flow -= tmp, ret += tmp;
if (!flow) break;
}
if (!ret) d[u] = -1;
return ret;
}
int Dinic() {
int ret = 0;
for (int i = s; i <= t; i++) cr[i] = pr[i];
while (BFS()) {
ret += DFS(s, INF);
for (int i = s; i <= t; i++) pr[i] = cr[i];
}
return ret;
}
int trans(int x, int y) {return x*m+y+1;}
int main() {
scanf("%d%d", &n, &m), init();
for (int i = 0; i < n; i++) {
for (int j = 1; j < m; j++) addedge(trans(i, j), trans(i, j-1), INF);
for (int j = 0, a, w, x, y; j < m; j++) {
scanf("%d%d", &a, &w), c[trans(i, j)] = a;
if (a > 0) addedge(trans(i, j), t, a); else addedge(s, trans(i, j), -a);
while (w--) scanf("%d%d", &x, &y), addedge(trans(i, j), trans(x, y), INF);
}
}
return topo(), printf("%d", sum-Dinic()), 0;
}