【洛谷题解/ZJOI2005】P2585 三色二叉树

原题链接:https://www.luogu.com.cn/problem/solution/P2585
难度:提高+/省选-
涉及知识点:树上DP

题意

给定一个“二叉树序列” S S S,要求根据题目描述建树。
建树规则
要求对二叉树进行染色,可以染成蓝色、绿色或红色,但是,一个节点的颜色不能与其子节点相同,求这棵二叉树最多和最少能被染成绿色。

分析与解决

先回顾一下“没有上司的舞会”一题的解决方法,如果一个父节点不选,我们就把该节点的每个子节点选或不选的开心值的较大 D P DP DP 值累加起来,如果一个父节点选,就把该节点的每个节点不选时的最大 D P DP DP 值累加起来,通过这样分类讨论的方式求出了答案,显然“三色二叉树”一题也是可以类比的。

首先定义 f [ i ] [ 0 / 1 ] f[i][0/1] f[i][0/1] 为节点 i i i 被染成绿色或不被染成绿色时,在其子树(包括自己)被染成绿色的最多节点数,0 表示不被染成绿色,1 表示被染成绿色。

一个节点是不是被染成蓝色或红色我们不关心,可以一概被归类到“除绿色外的其他颜色”,我们只关心一个节点被染或不染成绿色的情况,分下面两种情况讨论:

  • 如果该节点被染成绿色,那么就累加左右子节点不染绿色时的最大 f f f 值再加1(因为该节点染色了)
  • 如果节点不被染成绿色,那么就对左子节点染绿色和右子节点染绿色的两种情况取最值。

最后输出时,对根节点染成绿色和不染成绿色两种情况取最值即可。
至于建树,用递归方式,用数组存储,依次判断序列中的每个数并作出相应的处理。

AC代码

#include <iostream>
#include <cstdio>
#include <cstring>

using namespace std;

const int N = 5e5 + 10;
char s[N]; //二叉树序列
int cnt, n;
int tr[N][2]; //树
int f[N][2];

void dfs(int u)
{
    cnt++;
    if (s[u] == '0') return; //没有子节点
    else if (s[u] == '1')
    {
        tr[u][0] = u + 1; //左子节点
        dfs(u + 1);
    }
    else if (s[u] == '2')
    {
        tr[u][0] = u + 1; //左子节点
        dfs(u + 1);
        tr[u][1] = cnt + 1; //右子节点
        dfs(cnt + 1);
    }
}

int main()
{
    scanf("%s", s + 1);
    dfs(1); //递归建树
    n = strlen(s + 1);

    for (int i = n; i >= 1; i--)
    {
        //1.父节点染成绿色
        //2.左子节点或者右子节点染成绿色
        f[i][1] = f[tr[i][0]][0] + f[tr[i][1]][0] + 1;
        f[i][0] = max(f[tr[i][0]][1] + f[tr[i][1]][0], f[tr[i][0]][0] + f[tr[i][1]][1]);
    }
    cout << max(f[1][0], f[1][1]) << " ";
    for (int i = n; i >= 1; i--)
    {
        //1.父节点染成绿色
        //2.左子节点或者右子节点染成绿色
        f[i][1] = f[tr[i][0]][0] + f[tr[i][1]][0] + 1;
        f[i][0] = min(f[tr[i][0]][1] + f[tr[i][1]][0], f[tr[i][0]][0] + f[tr[i][1]][1]);
    }
    cout << min(f[1][0], f[1][1]);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

oier_Asad.Chen

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

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

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

打赏作者

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

抵扣说明:

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

余额充值