1. 程式人生 > 其它 >[HNOI2003] 消防局的設立

[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;
}
看不見我看不見我看不見我