原题链接: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 表示被染成绿色。
一个节点是不是被染成蓝色或红色我们不关心,可以一概被归类到“除绿色外的其他颜色”,我们只关心一个节点被染或不染成绿色的情况,分下面两种情况讨论:
最后输出时,对根节点染成绿色和不染成绿色两种情况取最值即可。
至于建树,用递归方式,用数组存储,依次判断序列中的每个数并作出相应的处理。
#include
#include
#include
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;
}