1. 程式人生 > >bzoj1304: [CQOI2009]葉子的染色

bzoj1304: [CQOI2009]葉子的染色

i++ clu tchar print 一個點 結點 desc int lang

又是一道優美的dp

Description

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

Input

第一行包含兩個正整數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 有邊相連。

Output

僅一個數,即著色結點數的最小值。

Sample Input

5 3
0
1
0
1 4
2 5
4 5
3 5

Sample Output

2

HINT

M<=10000

N<=5021


題目分析

這題的限制有些詭異,並且長得非常不像dp。仿佛我們需要三個狀態$f[i][0/1/2]$來表示當前塗色狀態,而且這樣轉移起來甚是麻煩。

但是細細一想,若一個點$node$有眾多子節點,那就意味著有眾多的葉子節點。而每一個葉子節點都是要上色的,況且上的色非黑即白就兩種。

那麽不論$node$子樹內白多黑多,$node$這個節點當然要承擔起父節點的責任,塗成一種顏色,這樣肯定是不會更差的。

於是只有兩個狀態的樹形dp就不難想到也不難打出了。

 1 /**************************************************************
 2     Problem: 1304
 3     User: AntiQuality
 4     Language: C++
 5     Result: Accepted
 6     Time:20 ms
 7     Memory:1600 kb
 8 ****************************************************************/
 9
10 #include<bits/stdc++.h> 11 const int maxn = 10035; 12 const int INF = 2e9; 13 14 int n,m,c[maxn]; 15 int f[maxn][2]; 16 int edgeTot,edges[maxn<<1],nxt[maxn<<1],head[maxn]; 17 18 int read() 19 { 20 char ch = getchar(); 21 int num = 0; 22 bool fl = 0; 23 for (; !isdigit(ch); ch = getchar()) 24 if (ch==-) fl = 1; 25 for (; isdigit(ch); ch = getchar()) 26 num = (num<<1)+(num<<3)+ch-48; 27 if (fl) num = -num; 28 return num; 29 } 30 void addedge(int u, int v) 31 { 32 edges[++edgeTot] = u, nxt[edgeTot] = head[v], head[v] = edgeTot; 33 edges[++edgeTot] = v, nxt[edgeTot] = head[u], head[u] = edgeTot; 34 } 35 void dp(int now, int fa) 36 { 37 if (now <= n){ 38 f[now][c[now]] = 1, f[now][1-c[now]] = INF; 39 return; 40 } 41 f[now][0] = 1, f[now][1] = 1; 42 for (int i=head[now]; i!=-1; i=nxt[i]) 43 { 44 int v = edges[i]; 45 if (v!=fa){ 46 dp(v, now); 47 f[now][0] += std::min(f[v][1], f[v][0]-1);  //可能這裏為什麽f[v][1]不加1,而是f[v][0]減1會有點疑問 48 f[now][1] += std::min(f[v][0], f[v][1]-1);  //f[now][]=1是每次dp時先確定好了的,這樣可以避免重復計算 49 }                             //手推一下就好了 50 } 51 } 52 int main() 53 { 54 memset(head, -1, sizeof head); 55 m = read(), n = read(); 56 for (int i=1; i<=n; i++) c[i] = read(); 57 for (int i=1; i<m; i++) addedge(read(), read()); 58 dp(m, 0); 59 printf("%d\n",std::min(f[m][0], f[m][1])); 60 return 0; 61 }

END

bzoj1304: [CQOI2009]葉子的染色