[HNOI2003] 消防局的设立
[HNOI2003] 消防局的设立
树形 \(DP\) , 其实这个题的贪心非常好想也非常好写, 但是为了练习树形 \(DP\) , 嘶~
这个题不算难, 但是我写了好久, 原因是写错下标了...
设状态 \(f[x][0/1/2/3/4]\) 表示以 \(x\) 为根节点, 覆盖到向上 \(2/1/0/-1/-2\) 层的最小消防站数.
转移稍微麻烦一点, 设 \(y\) 为 \(x\) 的儿子, 如果我们能够覆盖到向上 \(2\) 层, 那么我们当前位置一定有一个消防站, 为了让消防站数最少, 那么我们下面 \(4\) 层没有消防站, 所以我们从 \(f[y][4]\) 转移过来, \(f[x][0] = \sum f[y][4] + 1\) . 如果我们能覆盖到向上 \(1\) 层, 那么我们至少有 \(1\) 个儿子能覆盖到它的向上 \(2\) 层, 那么 \(f[x][1] = \sum f[y][3] - \max(f[y][3] - f[y][0])\) . 同理如果我们能覆盖自己, 那么我们至少有 \(1\) 个儿子能覆盖到它的向上 \(1\) 层, 那么 \(f[x][2] = \sum f[y][2] - \max(f[y][2] - f[y][1])\) .
\(code:\)
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int read() {
int x = 0, f = 1;
char ch = getchar();
while (!isdigit(ch)) {
if (ch == '-') f = -1;
ch = getchar();
}
while (isdigit(ch)) {
x = (x << 1) + (x << 3) + (ch ^ 48);
ch = getchar();
}
return x * f;
}
const int N = 1005, inf = 1 << 30;
int n, f[N][5];
int tot, to[N], nxt[N], head[N];
void add(int u, int v) {
to[++tot] = v, nxt[tot] = head[u], head[u] = tot;
}
void dfs(int x) {
int f1 = -inf, f2 = -inf; f[x][0] = 1;
for (int i = head[x]; i; i = nxt[i]) {
int y = to[i];
dfs(y);
f[x][0] += f[y][4];
f[x][1] += f[y][3]; f[x][2] += f[y][2];
f[x][3] += f[y][2]; f[x][4] += f[y][3];
f1 = max(f1, f[y][3] - f[y][0]);
f2 = max(f2, f[y][2] - f[y][1]);
}
if (!head[x]) f[x][1] = f[x][2] = 1;
else f[x][1] -= f1, f[x][2] -= f2;
for (int i = 1; i < 5; i++) f[x][i] = min(f[x][i], f[x][i - 1]);
}
int main() {
n = read();
for (int i = 2; i <= n; i++) {
int u = read();
add(u, i);
}
dfs(1);
printf("%d", f[1][2]);
return 0;
}
看不见我看不见我看不见我