[树形DP]叶子的颜色

题目描述

给一棵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  
 1<=N,M<=100000

分析

根据画图容易推出:
1、哪个节点作为根都(除叶节点)不会改变答案值
2、可以作为根的点必定互相可以到达
那么我们随便找一个点开始DFS(如n+1)
容易设出规划数组:fi,j表示第i个节点染成j色的最少染点数量
根据题目,我们可以转换一下意思,即fi,j表示以第i个节点为根的子树染成j色以后,这棵子树要染色的最少点数
容易知道初始值应该为1(因为自己染了嘛)
然后如果是叶节点,为了满足本身的颜色限制,明显对于这个叶节点的fi,j,j如果和叶节点的颜色限制相反的话,值为∞
然后状态转移方程也很容易推:
f[u,j]+=min{f[v,j]-1,f[v,!j]
为什么-1呢?因为同色的话染了根就没必要染下面的节点,所以-1

#include <iostream>
#include <cstdio>
#define rep(i,a,b) for (i=a;i<=b;i++)
const int N=100001;
using namespace std;
int f[N][2];
int m,n,a[N];
struct Edge {
    int u,v,nx;
}g[2*N];
int cnt,list[N];

void Addedge(int u,int v) {
    g[++cnt].u=u;g[cnt].v=v;g[cnt].nx=list[u];list[u]=cnt;
    g[++cnt].u=v;g[cnt].v=u;g[cnt].nx=list[v];list[v]=cnt;
}

void Dfs(int x,int F) {
    int i;
    f[x][0]=f[x][1]=1;
    if (x<=n)
    f[x][!a[x]]=2147483647;
    for (i=list[x];i;i=g[i].nx)
    if (g[i].v!=F) {
        Dfs(g[i].v,x);
        f[x][0]+=min(f[g[i].v][0]-1,f[g[i].v][1]);
        f[x][1]+=min(f[g[i].v][1]-1,f[g[i].v][0]);
    }
}

int main() {
    int i;
    scanf("%d%d",&m,&n);
    rep(i,1,n)
    scanf("%d",&a[i]);
    rep(i,1,m-1) {
        int u,v;
        scanf("%d%d",&u,&v);
        Addedge(u,v);
    }
    Dfs(n+1,0);
    printf("%d",min(f[n+1][0],f[n+1][1]));
}

猜你喜欢

转载自blog.csdn.net/ssl_qyh0ice/article/details/80951602