题目让我们求最小染色数量,很容易想到dp。
设 $f_{i, 1/0}$ 表示第i个点染黑、白的最小数量, 初始值均为1(自己一条路)。如果这个点为叶子节点,即已经规定了第一个染色点,那么相反颜色的f因设为inf(不能选择它)。
那么,如何选根? 看下图(转载的hhh),无论我们选择1节点还是2节点,可以发现,只要不是叶子节点,根的变化对答案没有影响。
那么,我们就可以很愉快地得到状态转移方程了:
$f_{u, 0} += min(f_{v,1}, f_{v, 0} - 1)$
$f_{u,1} += min (f_{v,0}, f_{v,1} - 1)$
代码:
#include <bits/stdc++.h> using namespace std; #define N 100010 const int inf = (int)1e10; inline int read(){ int x = 0, s = 1; char c = getchar(); while(!isdigit(c)){ if(c == '-') s = -1; c = getchar(); } while(isdigit(c)){ x = x * 10 + (c ^ '0'); c = getchar(); } return x * s; } struct node{ int v, next; } t[N << 1]; int f[N]; int dp[N][2]; int bian = 0; inline void add(int u, int v){ t[++bian] = (node){v, f[u]}, f[u] = bian; t[++bian] = (node){u, f[v]}, f[v] = bian; return ; } int c[N]; int n, m; void dfs(int now, int fa){ dp[now][0] = dp[now][1] = 1; if(now <= m){ if(c[now] == 0) dp[now][1] = inf; else dp[now][0] = inf; } for(int i = f[now]; i; i = t[i].next){ int v = t[i].v; if(v != fa){ dfs(v, now); dp[now][0] += min(dp[v][1], dp[v][0] - 1); dp[now][1] += min(dp[v][0], dp[v][1] - 1); } } return ; } int main(){ n = read(), m = read();// n 节点总数 m: 叶子 for(int i = 1;i <= m; i++) c[i] = read(); for(int i = 1;i < n; i++){ int x = read(), y = read(); add(x , y); } dfs(m + 1, m + 1); int ans = min(dp[m+1][0], dp[m+1][1]); cout << ans << endl; return 0; }