BZOJ1304 CQOI2009 叶子的染色 【树形DP】

BZOJ1304 CQOI2009 叶子的染色


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

M<=10000
N<=5021


探索一下性质,发现无论根在哪里对答案是没有影响的

然后就可以随便选定一个根,然后就做完了

转移的时候考虑当前放什么,在DP的时候如果子节点的颜色和当前点的颜色一样,当前点子节点是不用放的,所以在统计的时候–就好了

dp[i][1/0]表示以i为根的子树中,i染成1/0的最小染色数量


#include<bits/stdc++.h>
using namespace std;
#define N 100010
#define INF 0x3f3f3f3f
struct Edge{int v,next;}E[N<<1];
int head[N],tot=0;
int f[N][2],c[N];
int n,m;
inline int read(){
    int ans=0,w=1;char c=getchar();
    while(!isdigit(c)&&c!='-')c=getchar();
    if(c=='-')w=-1,c=getchar();
    while(isdigit(c))ans=(ans<<1)+(ans<<3)+c-'0',c=getchar();
    return ans*w;
}
inline void add(int u,int v){
    E[++tot]=(Edge){v,head[u]};
    head[u]=tot;
}
inline void dfs(int u,int fa){
    if(u<=n){
        f[u][c[u]]=1;
        f[u][c[u]^1]=INF;
        return;
    }
    f[u][0]=f[u][1]=1;
    for(int i=head[u];i;i=E[i].next){
        int v=E[i].v;
        if(v==fa)continue;
        dfs(v,u);
        f[u][0]+=min(f[v][1],f[v][0]-1);
        f[u][1]+=min(f[v][0],f[v][1]-1);
    }
}
int main(){
    freopen("1304.in","r",stdin);
    m=read();n=read();
    for(int i=1;i<=n;i++)c[i]=read();
    for(int i=1;i<m;i++){
        int u=read(),v=read();
        add(u,v);
        add(v,u);
    }
    dfs(n+1,0);
    printf("%d",min(f[n+1][0],f[n+1][1]));
    return 0;
}
posted @ 2018-08-04 10:46  Dream_maker_yk  阅读(162)  评论(0编辑  收藏  举报