[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;
}
posted @ 2021-08-24 18:31  sshadows  阅读(24)  评论(0编辑  收藏  举报