树形dp(二)——叶子的染色(Loj 10161)

题目链接:https://loj.ac/problem/10161
题目大意
给一棵m个结点的根树,你可以选择一个度数大于1的结点作为根,然后给一些结点(根、内部结点和叶子均可)着以黑色或白色。你的着色方案应该保证根结点到每个叶子的简单路径上都至少包含一个有色结点(哪怕是这个叶子本身)。 对于每个叶结点u,定义c[u]为从根结点从U的简单路径上最后一个有色结点的颜色。给出每个c[u]的值,设计着色方案,使得着色结点的个数尽量少。
解题思路
这题的关键就是dp的设计,一般来说树形dp都是以某点为根的子树的状态转移。这题也不例外,就是状态有点难想。dp[i][0]表示以i为根的子树最后一个点想要得到一个黑色祖先的最小代价,dp[i][1]表示以i为根的子树得到一个白色祖先的最小代价。那么状态转移方程如下。
在这里插入图片描述
初态dp[u][0]=dp[u][1]=1。
我们向父节点转移的时候,如果说父节点和子节点颜色一样不用改变,如果子节点要与父节点颜色不一样,就需要花费一个代价去改变子节点。
AC代码

#include <iostream>
#include <stdio.h>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxn=1e4+5;
const int inf=0x3f3f3f3f;
struct node
{
    int to,next;
}edge[maxn<<1];
int head[maxn];
//dp[i][0]以i为根的子树i祖先为黑色最小代价
//dp[i][1]以i为根的子树i祖先为白色最小代价
int dp[maxn][2];
int m,n,cnt;
int a[maxn];
void add(int x,int y)
{
    edge[++cnt].to=y;
    edge[cnt].next=head[x];
    head[x]=cnt;
}
void dfs(int u,int fa)
{
    dp[u][0]=dp[u][1]=1;
    if(u<=n)
    {
        if(!a[u])
        dp[u][1]=inf;
        else
        dp[u][0]=inf;
    }
    for(int i=head[u];i;i=edge[i].next)
    {
        int v=edge[i].to;
        if(v==fa)
        continue;
        dfs(v,u);
        dp[u][0]+=min(dp[v][0]-1,dp[v][1]);
        dp[u][1]+=min(dp[v][1]-1,dp[v][0]);
    }
}
int main()
{
    scanf("%d%d",&m,&n);
    for(int i=1;i<=n;++i)
    scanf("%d",&a[i]);
    int x,y;
    for(int i=1;i<m;++i)
    {
        scanf("%d%d",&x,&y);
        add(x,y);
        add(y,x);
    }
    dfs(n+1,0);
    printf("%d\n",min(dp[n+1][0],dp[n+1][1]));
    return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_44491423/article/details/108106692