1. 程式人生 > 實用技巧 >[CQOI2009]葉子的染色

[CQOI2009]葉子的染色

首先,選擇任意一個度數大於\(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;
}

```