【蓝桥杯集训·每日一题】AcWing 1079. 叶子的颜色

一、题目

1、原题链接

1079. 叶子的颜色

2、题目描述

给一棵有 m 个节点的无根树,你可以选择一个度数大于 1 的节点作为根,然后给一些节点(根、内部节点、叶子均可)着以黑色或白色。

你的着色方案应保证根节点到各叶子节点的简单路径上都至少包含一个有色节点,哪怕是这个叶子本身。

对于每个叶子节点 u,定义 cu 为从根节点到 u 的简单路径上最后一个有色节点的颜色。

给出每个 cu 的值,设计着色方案使得着色节点的个数尽量少

输入格式

第一行包括两个数 m,n,依次表示节点总数和叶子个数,节点编号依次为 1 至 m,其中编号 1 至 n 是叶子。

接下来 n 行每行一个 0 或 1 的数,其中 0 表示黑色,1 表示白色,依次为 c1,c2,…,cn 的值。

接下来 m−1 行每行两个整数 a,b,表示节点 a 与 b 有边相连。

输出格式

输出仅一个数,表示着色节点数的最小值。

数据范围
在这里插入图片描述
输入样例

5 3
0
1
0
1 4
2 5
4 5
3 5

输出样例

2

二、解题报告

1、思路分析

思路来源:y总讲解视频
y总yyds

(1)题目要求:染色方法满足每个叶子节点到根节点的路径上至少存在一个结点的颜色为c[i]
(2)首先给出两个结论(详细证明见y总讲解视频):

  • 无论以哪一个度大于1的结点作为根节点,最优解不变
  • 每个节点染色比不染色时的解更优

(3)dp[i][无色]dp[i][白色]dp[i][黑色]分别表示以i为根节点并且i为无色、白色、黑色时着色节点的最小数量。s[j]i的每一棵子树的根节点。

  • dp[i][无色]+=min(dp[s[j]][白色],dp[s[j]][黑色]):由于i没有染色,所以要满足条件时,其子树的根节点需要染色,染成黑色或白色,取最其中最小值。
  • dp[i][白色]+=min(dp[s[j]][白色]-1,dp[s[j]][黑色])+1:由于i已经染成了白色,所以要满足条件时,其子树根节点如果要染成白色的话,可以省去该次操作,因为该路径上已经有一个白色的节点了,如果要染成黑色的话则需要染该子树的根节点,因为该路径上没有黑色的节点,两者取最小值,最后再加一次操作(i这个根节点染色需要一次操作)。
  • dp[i][黑色]+=min(dp[s[j]][白色],dp[s[j]][黑色]-1)+1:同理上面一种情况。
  • 观察转移方程,后两种的染色次数的最小值小于等于第一种情况,所以可以只从后两种情况中进行转移求最优解即可。

2、时间复杂度

时间复杂度为O(n+m)

3、代码详解

#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
const int N=10010,M=2*N,INF=1e8;
int m,n;
int h[N],e[M],ne[M],idx;   //邻接表存储树
int dp[N][2];
int c[N];
//邻接表加边
void add(int a,int b){
    e[idx]=b;
    ne[idx]=h[a];
    h[a]=idx++;
}
//dfs递归处理每个子树
void dfs(int u,int fa){
    if(u<=n) return ;
    dp[u][0]=dp[u][1]=1;    //染当前点需要一次操作
    //遍历u的每条边
    for(int i=h[u];i!=-1;i=ne[i]){
        int j=e[i];
        if(j==fa) continue;   //如果是j是根节点continue
        dfs(j,u);       //递归处理子树
        dp[u][0]+=min(dp[j][0]-1,dp[j][1]);   //染成白色的转移方程
        dp[u][1]+=min(dp[j][1]-1,dp[j][0]);   //染成黑色的转移方程
    }
}
int main(){
    cin>>m>>n;
    memset(h,-1,sizeof h);
    for(int i=1;i<=n;i++) cin>>c[i];
    for(int i=1;i<=n;i++){
        dp[i][c[i]]=1;    //当前叶节点为要求的颜色,最小染色次数为1
        dp[i][!c[i]]=INF; //否则无解,设置为正无穷
    }
    for(int i=0;i<m-1;i++){
        int a,b;
        cin>>a>>b;
        add(a,b),add(b,a);
    }
    dfs(n+1,-1);     //任意选一个根进行dfs
    cout<<min(dp[n+1][0],dp[n+1][1]);
    return 0;
}

三、知识风暴

树形DP

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

马看到什么是人决定的

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值