[树形dp] Jzoj P1010 叶子的颜色

Description

  给一棵m个结点的无根树,你可以选择一个度数大于1的结点作为根,然后给一些结点(根、内部结点和叶子均可)着以黑色或白色。你的着色方案应该保证根结点到每个叶子的简单路径上都至少包含一个有色结点(哪怕是这个叶子本身)。
  对于每个叶结点u,定义c[u]为从u到根结点的简单路径上第一个有色结点的颜色。给出每个c[u]的值,设计着色方案,使得着色结点的个数尽量少。
 

Input

  第一行包含两个正整数m, n,其中n是叶子的个数,m是结点总数。结点编号为1,2,…,m,其中编号1,2,… ,n是叶子。以下n行每行一个0或1的整数(0表示黑色,1表示白色),依次为c[1],c[2],…,c[n]。以下m-1行每行两个整数a,b(1<=a < b <= m),表示结点a和b 有边相连。

Output

  仅一个数,即着色结点数的最小值。
 

Sample Input

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

Sample Output

2
 

Hint

【数据范围】  
  1<=N,M<=100000

题解

  • 其实这题一直在纠结选哪个作根
  • 其实知道根后,就是一个典型的树形dp
  • 设f[i][0/1]为以i为根的子树i点染0/1色的最少染色点数
  • 那么叶子的点的初始值就是要染的颜色为1不染的INF
  • 现在考虑一下选哪个为根
  • 现在我们不知道根节点是什么?换根么?再仔细思考一下...其实可以证明无论选择谁做根结点都是一样的

    (其实换根也是可做滴!!) 

  • 因为对于两个相邻的点,首先这两个点颜色不可能相同,如果相同就不满足最小染色点数
  • 如果不相同,换了也好像没有关系......
  • 感性理解一波,选一个不是叶子的结点做根的答案都是一样的

代码

 1 #include<cstdio>
 2 #include<iostream>
 3 using namespace std;
 4 const int inf=1000000000;
 5 int n,m,c[100010],f[100010][2],to[200010],from[200010],head[100010],cnt;
 6 void insert(int x,int y) { to[++cnt]=y; from[cnt]=head[x]; head[x]=cnt; }
 7 void dp(int root,int fa)
 8 {
 9     f[root][0]=f[root][1]=1;
10     if (root<=m) if (c[root]) f[root][0]=inf; else f[root][1]=inf;
11     for (int i=head[root];i;i=from[i])
12         if (to[i]!=fa)
13         {
14             dp(to[i],root);
15             f[root][0]+=min(f[to[i]][0]-1,f[to[i]][1]);
16             f[root][1]+=min(f[to[i]][1]-1,f[to[i]][0]);
17         }
18 }
19 int main()
20 {
21     scanf("%d%d",&n,&m);
22     for (int i=1;i<=m;i++) scanf("%d",&c[i]);
23     for (int i=1;i<=n-1;i++)
24     {
25         int x,y;
26         scanf("%d%d",&x,&y);
27         insert(x,y);
28         insert(y,x);
29     }
30     dp(m+1,-1);
31     printf("%d",min(f[m+1][1],f[m+1][0]));
32     return 0;
33 }

猜你喜欢

转载自www.cnblogs.com/Comfortable/p/9277406.html
今日推荐