首先,选择任意一个度数大于(1)的节点为根的最优的答案都是固定的,具体证明这里不加赘述。
我们仔细研究,他只要求根节点到叶子节点的最后一个有色节点的颜色。我们对第(x)号节点染色,意味着我们把所有它子树中的叶子节点最近的一个有色节点的颜色就发生了改变。显然,儿子越多的节点性价比越高。
因此,我们定义(dp_{i,0/1})为第(i)号节点染成第(j)种颜色的最小花费,那么对于(j)号节点是他的儿子,可以把他的儿子染成异色,也可以让儿子的颜色与他一致,有转移方程如下:
(dp_{i,0}) += (min(dp_{j,0} - 1 , dp_{j,1}))
(dp_{i,1}) += (min(dp_{j,1} - 1 , dp_{j,0}))
```cpp
#include <cstdio>
#include <algorithm>
using namespace std;
const int MAXN = 1e4 + 5;
int head[MAXN] , to[MAXN << 1] , nxt[MAXN << 1] , cnt , n , m;
int c[MAXN] , dp[MAXN][2];
void add(int x , int y){nxt[++cnt] = head[x];head[x] = cnt;to[cnt] = y;}
void read(int &x) {
int f = 1;x = 0;char s = getchar();
while(s < '0' || s > '9') {if(s == '-') f = -1;s = getchar();}
while(s >= '0' && s <= '9') {x = (x << 3) + (x << 1) + s - '0';s = getchar();}
x *= f;
}
void dfs(int x , int fa) {
if(x <= n) {
dp[x][c[x]] = 1; // 染上的花费
dp[x][!c[x]] = 1e9; // 不能染这种颜色,设为无穷大
return;
}
dp[x][0] = dp[x][1] = 1;
for (int i = head[x]; i; i = nxt[i]) {
if(to[i] == fa) continue;
dfs(to[i] , x);
dp[x][0] += min(dp[to[i]][0] - 1 , dp[to[i]][1]);
dp[x][1] += min(dp[to[i]][0] , dp[to[i]][1] - 1);
//转移,如上
}
}
int main() {
read(m);read(n);
for (int i = 1; i <= n; ++i) read(c[i]);
for (int i = 1; i < m; ++i) {
int x , y;
read(x);read(y);
add(x , y);
add(y , x);
}
dfs(m , m);//姑且选m为根节点
printf("%d" , min(dp[m][0] , dp[m][1]));
return 0;
}
```