天天看点

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

1

1 4

2 5

4 5

3 5

Sample Output

2

HINT

M<=10000

N<=5021

解法:树形DP。

dp[i][0/1]表示以i为根节点的子树i染成0/1的最少染色节点数

然后转移的话就比较简单。

关键点在于这个树形DP,如何选根的问题。其实可以证明除了选叶子节点的任何一个节点做根节点都是可以

的。比如现在有1,2两个节点都可以作为根,那么,首先这两个节点颜色不可能相同,如果相同那么把一个变

成透明的一定更优,所以无论12哪个为根节点ans不变…

如果这两个节点的颜色本来就不同,那么根节点选1或者2队答案更没有影响了,所以我们选一个叶子节点之外

的节点当成根节点就可以。

//BZOJ 1304

#include <bits/stdc++.h>
using namespace std;

const int inf = ;
const int maxn = ;
int n, m, c[maxn];
int dp[maxn][]; //dp[i][0/1]表示以i为根节点的子树i染成0/1的最少染色节点数
int head[maxn], edgecnt;

struct node{
    int v, nxt;
    node(){}
    node(int v, int nxt) : v(v), nxt(nxt) {}
}E[maxn*];

void init(){
    edgecnt = ;
    memset(head, -, sizeof(head));
}

void addedge(int u, int v){
    E[edgecnt].v = v, E[edgecnt].nxt = head[u], head[u] = edgecnt++;
}

void dfs(int u, int fa){
    dp[u][]=dp[u][]=;
    if(u <= m){
        if(c[u]==) dp[u][]=inf;
        else dp[u][]=inf;
    }
    for(int i=head[u];~i;i=E[i].nxt){
        if(E[i].v!=fa){
            dfs(E[i].v, u);
            dp[u][]+=min(dp[E[i].v][]-, dp[E[i].v][]);
            dp[u][]+=min(dp[E[i].v][]-, dp[E[i].v][]);
        }
    }
}

int main()
{
    init();
    scanf("%d%d", &n, &m);
    for(int i=; i<=m; i++) scanf("%d", &c[i]);
    for(int i=; i<n; i++){
        int x, y;
        scanf("%d%d", &x, &y);
        addedge(x, y);
        addedge(y, x);
    }
    dfs(m+, -);
    int ans = min(dp[m+][], dp[m+][]);
    printf("%d\n", ans);
    return ;
}