叶子的颜色

发布时间 2023-08-19 11:39:21作者: onlyblues

叶子的颜色

给一棵有 $m$ 个节点的无根树,你可以选择一个度数大于 $1$ 的节点作为根,然后给一些节点(根、内部节点、叶子均可)着以黑色或白色。

你的着色方案应保证根节点到各叶子节点的简单路径上都至少包含一个有色节点,哪怕是这个叶子本身。

对于每个叶子节点 $u$,定义 $c_u$ 为从根节点到 $u$ 的简单路径上最后一个有色节点的颜色。

给出每个 $c_u$ 的值,设计着色方案使得着色节点的个数尽量少。

输入格式

第一行包括两个数 $m,n$,依次表示节点总数和叶子个数,节点编号依次为 $1$ 至 $m$,其中编号 $1$ 至 $n$ 是叶子。

接下来 $n$ 行每行一个 $0$ 或 $1$ 的数,其中 $0$ 表示黑色,$1$ 表示白色,依次为 $c_1,c_2, \ldots, c_n$ 的值。

接下来 $m−1$ 行每行两个整数 $a,b$,表示节点 $a$ 与 $b$ 有边相连。

输出格式

输出仅一个数,表示着色节点数的最小值。

数据范围

数据 $1$ $2$ $3$ $4$ $5$ $6$ $7$ $8$ $9$ $10$
$M$ $10$ $50$ $100$ $200$ $400$ $1000$ $4000$ $8000$ $10000$ $10000$
$N$ $5$ $23$ $50$ $98$ $197$ $498$ $2044$ $4044$ $5021$ $4996$

输入样例:

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

输出样例:

2

 

解题思路

  首先一定是有解的,先随便选择一个度数大于$1$的节点作为整棵树的根节点,然后直接对每个叶子节点$u$涂上对应的颜色$c_u$即可。不过可以发现这肯定不是最优的解法,假如有$k$个叶子的父节点相同,并且这$k$个叶子节点中有$x$个被染成白色,$y$个染成黑色(满足$x+y=k$),那么我们可以把这个父节点染成$x$和$y$中最大的那个所对应的颜色,然后再将这个颜色的叶子改成透明,这样染色的节点数目就变成了$k-\max\{x,y\}+1$,比原来的$k$要小。以此类推,从叶子不断往上重复这个步骤,直到根节点。

  以样例为例,选取$5$号点作为根节点,如下图:

  很明显当儿子染成白色和黑色的数目不相同时,父亲应该染成颜色最多的那个更优。而如果儿子染成黑白的数目相同时,父亲染成这两种颜色都是最优解,这时情况就不唯一了,最终父亲要染成什么颜色还要取决于与它同层的兄弟节点染成什么颜色最优。这当然可以通过枚举所有的情况然后取最优解,但我们可以用dp来避免重复枚举。

  可以发现当我们往上刚走到父节点时,此时所有的儿子都是染了色的,经过操作后父节点也必然会染色(相应的儿子改为透明)。因此定义状态$f(u,j)$表示考虑以$u$为根的子树,且$u$被染成颜色$j$时的所有合法方案的染色最小数目。由于$u$的每棵子树是相互独立的,且儿子$v_i$也必然染了颜色,因此状态转移方程就是$$f(u,j) = 1 + \sum\limits_{i=1}^{k}{\min\left\{f(v_i,j)-1, f(v_i,\bar{j})\right\}}$$

  其中加$1$是因为$u$要染色,$f(v_i,j)-1$表示儿子与父亲染成一样的颜色,因此儿子改成透明,$f(v_i,\bar{j})$表示儿子与父亲染的颜色不同。

  最后我们只需任选一个度数大于$1$的节点作为根节点跑一遍dfs即可,因为对于任意一个根节点分析的方法是一样的,得到的最优解也是一样的。

  AC代码如下,时间复杂度为$O(n+m)$:

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 
 4 const int N = 1e4 + 10, M = N * 2;
 5 
 6 int n, m;
 7 int c[N];
 8 int head[N], e[M], ne[M], idx;
 9 int f[N][2];
10 
11 void add(int v, int w) {
12     e[idx] = w, ne[idx] = head[v], head[v] = idx++;
13 }
14 
15 void dfs(int u, int pre) {
16     if (u <= m) {
17         f[u][c[u]] = 1;
18         return;
19     }
20     f[u][0] = f[u][1] = 1;
21     for (int i = head[u]; i != -1; i = ne[i]) {
22         if (e[i] != pre) {
23             dfs(e[i], u);
24             f[u][0] += min(f[e[i]][0] - 1, f[e[i]][1]);
25             f[u][1] += min(f[e[i]][1] - 1, f[e[i]][0]);
26         }
27     }
28 }
29 
30 int main() {
31     scanf("%d %d", &n, &m);
32     for (int i = 1; i <= m; i++) {
33         scanf("%d", c + i);
34     }
35     memset(head, -1, sizeof(head));
36     for (int i = 0; i < n - 1; i++) {
37         int v, w;
38         scanf("%d %d", &v, &w);
39         add(v, w), add(w, v);
40     }
41     memset(f, 0x3f, sizeof(f));
42     dfs(n, -1);
43     printf("%d", min(f[n][0], f[n][1]));
44     
45     return 0;
46 }

 

参考资料

  [CQOI2009] 叶子的颜色 解题报告(树形DP) :https://www.cnblogs.com/xxzh/p/9278487.html

  AcWing 1079. 叶子的颜色(蓝桥杯集训·每日一题):https://www.acwing.com/video/4671/