BZOJ_1304_[CQOI2009]叶子的染色_树形DP

BZOJ_1304_[CQOI2009]叶子的染色_树形DP

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


考虑根节点可以扭成相邻的一个节点,交换一下两个节点的颜色,在最优解的情况下答案是不会变的。

因此根可以任取一个非叶子节点。

然后考虑一个点的子树可能有一些还没被祖先盖上,这些点的颜色一定是一样的。

于是设f[i]表示子树全部合法的最小代价,g[i]表示剩下一些白的,h[i]表示剩下一些黑的。

转移推一下吧,感觉还算可以。

代码:

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define N 10050
int head[N],to[N<<1],nxt[N<<1],cnt,n,m,c[N],f[N],g[N],h[N];
inline void add(int u,int v) {
    to[++cnt]=v; nxt[cnt]=head[u]; head[u]=cnt;
}
void dfs(int x,int y) {
    int i;
    if(x<=n) {
        f[x]=1; if(!c[x]) g[x]=0; else h[x]=0; return ;
    }
    int h1=0,h2=0,h3=0;
    for(i=head[x];i;i=nxt[i]) {
        if(to[i]!=y) {
            dfs(to[i],x);
            h1+=f[to[i]]; h2+=min(f[to[i]],g[to[i]]); h3+=min(f[to[i]],h[to[i]]);
        }
    }
    f[x]=min(min(h1,h2+1),h3+1);
    g[x]=h2; h[x]=h3;
}
int main() {
    memset(f,0x3f,sizeof(f));
    memset(g,0x3f,sizeof(g));
    memset(h,0x3f,sizeof(h));
    scanf("%d%d",&m,&n);
    int i,x,y;
    for(i=1;i<=n;i++) scanf("%d",&c[i]);
    for(i=1;i<m;i++) scanf("%d%d",&x,&y),add(x,y),add(y,x);
    dfs(n+1,0);
    printf("%d
",f[n+1]);
}
原文地址:https://www.cnblogs.com/suika/p/9464999.html