1. 程式人生 > >bzoj1304: [CQOI2009]葉子的染色(樹形DP)

bzoj1304: [CQOI2009]葉子的染色(樹形DP)

bzoj1304

題目描述:給一棵m個結點的無根樹,你可以選擇一個度數大於1的結點作為根,然後給一些結點(根、內部結點和葉子均可)著以黑色或白色。你的著色方案應該保證根結點到每個葉子的簡單路徑上都至少包含一個有色結點(哪怕是這個葉子本身)。 對於每個葉結點u,定義c[u]為從根結點從U的簡單路徑上最後一個有色結點的顏色。給出每個c[u]的值,設計著色方案,使得著色結點的個數儘量少。

輸入格式:第一行包含兩個正整數m, n,其中n是葉子的個數,m是結點總數。結點編號為1,2,…,m,其中編號1,2,… ,n是葉子。以下n行每行一個0或1的整數(0表示黑色,1表示白色),依次為c[1],c[2],…,c[n]。以下m-1行每行兩個整數a,b(1<=a < b <= m),表示結點a和b 有邊相連。

輸出格式:僅一個數,即著色結點數的最小值。

輸入樣例:
5 3
0
1
0
1 4
2 5
4 5
3 5

輸出樣例:
2

解析:算是比較簡單的吧。
   其實根節點的位置可以隨便選,因為選在哪裡對答案都沒有影響。
   之後就是一個簡單的DP。看到很多人將節點無色的情況也考慮進去了,其實考不考慮對答案沒有影響,因為最後的DP做出的答案是保證合法的。
   所以最後dp[0/1]進行DP就好了。

程式碼如下:

#include<cstdio>
#include<algorithm>
using namespace std;

const int maxn = 1e4 + 5;
int m, n, c[maxn], ans, dp[maxn][2];
int nxt[maxn << 1], hed[maxn << 1], to[maxn << 1], cnt;

int read(void) {
    char c; while (c = getchar(), c < '0' || c >'9'); int x = c - '0';
    while (c = getchar(), c >= '0' && c <= '9') x = x * 10 + c - '0'; return x;
}

void add(int x, int y) {
    nxt[++ cnt] = hed[x]; hed[x] = cnt; to[cnt] = y;
}

void treedp(int u, int pre) {
    dp[u][0] = dp[u][1] = 1; //對非葉子節點初始化 
    for (int i = hed[u]; i ; i = nxt[i]) {
      int v = to[i];
        if (v == pre) continue;
      treedp(v, u);
      dp[u][0] += min(dp[v][0] - 1, dp[v][1]);
      dp[u][1] += min(dp[v][1] - 1, dp[v][0]);
    }
    if (u <= n) { //對葉子節點初始化 
      dp[u][c[u]] = 1; dp[u][c[u] ^ 1] = 2e9;
    }
}

int main() {
    m = read(); n = read();
      for (int i = 1; i <= n; ++ i) c[i] = read();
      for (int i = 1; i < m; ++ i) {
        int x = read(), y = read();
        add(x, y); add(y, x); 
      }
    treedp(n + 1, 0);
    printf("%d", min(dp[n + 1][0], dp[n + 1][1]));
    return 0;
}