(JZ1010)2018.07.07【2018提高组】模拟B组 0.【CQOI2009】叶子的颜色【推荐】

【CQOI2009】叶子的颜色【推荐】 (Standard IO)

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

Data Constraint

Hint

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

题解:

    emmmmm,首先要明白,本题是树形动态规划(虽然我之前没学过就对了⁄(⁄ ⁄•⁄ω⁄•⁄ ⁄)⁄)
    树形动态规划的思想一般是先dfs一波(深搜),遍历完之后再去更新上面的结点的。
   遍历的话,我们可以通过建立各个点之间的联系来往下遍历;dp的话,我们可以用f[i][1/0]表示以i为根结点的子树的染黑/白色结点的最小值。
   如果当前结点涂成黑色,那么它的子树就可以少一个结点涂黑色,这样是不会增大我们的最小值的,这就可以用它的子树来更新它;抑或它的子树是白色的,要涂黑色就将结果+1即可。
   便可以推出f[x][0] += min(f[to[i]][0] , f[to[i]][1] + 1);白色同理,即可得f[x][1] += min(f[to[i]][1] , f[to[i]][0] + 1);

#include <cstdio>
#include <algorithm>
#define N 100000
using namespace std;
int f[N][2],head[N],to[N],next[N],cnt;
inline void add(int x , int y)
{
    cnt++;
    to[cnt] = y , next[cnt] = head[x] , head[x] = cnt;
}
void dfs(int x , int y)
{
    int i=head[x];
    while (i != 0)//遍历
     {
        if(to[i] != y){
             dfs(to[i] , x);
             f[x][0] += min(f[to[i]][0] , f[to[i]][1] + 1);//dp
             f[x][1] += min(f[to[i]][1] , f[to[i]][0] + 1);  
        }
        i=next[i];
     }      
}
int main()
{
    int n , m , i , x , y;
    scanf("%d%d" , &m , &n);
    for(i = 1 ; i <= n ; i ++ ) {
        scanf("%d" , &x);  
        if (x==1) f[i][0] = m;//初始化,放最大
             else f[i][1] = m;
    }
    for(i = 1 ; i < m ; i ++ ) scanf("%d%d" , &x , &y) , add(x , y) , add(y , x);//建立各个点之间的关系
    dfs(m , 0);
    printf("%d\n" , min(f[m][0] , f[m][1]) + 1);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/ssl_lzx/article/details/80956510
今日推荐