bzoj 1304 [CQOI 2009] 叶子的染色 - 动态规划
题目传送门
快速的传送门
慢速的传送门
题目大意
给定一棵无根树,每个点可以染成黑色或者白色,第$i$叶节点到根的路径上最后有颜色的点必须为$c_{i}$(叶节点可以染色)。问最少要染颜色的点的个数。
假设有根。显然动态规划。用$f[i][0 / 1]$表示考虑到$i$号点的颜色染成什么,它子树内的点最少要染多少个。
这里考虑使染色的深度尽量小(相当于钦定根节点染色,没有影响的),方便转移。枚举根节点染什么颜色。如果子树的根的颜色和它一样,那个点就没必要染色了。
于是愉快地解决了有根的时候。
对于无根的时候可以枚举根,记录转移的前缀和可以快速通过一条边转移根并计算上面的动态规划值。
其实根本没必要枚举根。
因为根在哪最有答案不会改变。
你可以考虑根所在的一条链。动态规划的某个方案都会钦定每个点都染成某个颜色。
无论根在哪,一段连续染成相同颜色的点都可以只保留最浅的一个点的染色。
所以直接找非叶节点动态规划。
Code
1 /** 2 * bzoj 3 * Problem#1304 4 * Accepted 5 * Time: 48ms 6 * Memory: 1600k 7 */ 8 #include <bits/stdc++.h> 9 using namespace std; 10 11 const int N = 1e4 + 5; 12 13 int m ,n; 14 int col[N]; 15 int f[N][2]; 16 vector<int> g[N]; 17 18 inline void init() { 19 scanf("%d%d", &m, &n); 20 for (int i = 1; i <= n; i++) 21 scanf("%d", col + i); 22 for (int i = 1, u, v; i < m; i++) { 23 scanf("%d%d", &u, &v); 24 g[u].push_back(v); 25 g[v].push_back(u); 26 } 27 } 28 29 void dfs(int p, int fa) { 30 if (p <= n) { 31 f[p][col[p]] = 1; 32 f[p][col[p] ^ 1] = 20010910; 33 return ; 34 } 35 f[p][0] = f[p][1] = 1; 36 for (int i = 0; i < (signed) g[p].size(); i++) { 37 int e = g[p][i]; 38 if (e == fa) continue; 39 dfs(e, p); 40 f[p][0] += min(f[e][0] - 1, f[e][1]); 41 f[p][1] += min(f[e][1] - 1, f[e][0]); 42 } 43 } 44 45 inline void solve() { 46 dfs(m, 0); 47 printf("%d\n", min(f[m][0], f[m][1])); 48 } 49 50 int main() { 51 init(); 52 solve(); 53 return 0; 54 }