前言
典型的树上dp。
题目大意
你的任务是要对一棵二叉树的节点进行染色。每个节点可以被染成红色、绿色或蓝色。并且,一个节点与其子节点的颜色必须不同,如果该节点有两个子节点,那么这两个子节点的颜色也必须不同。给定一颗二叉树的二叉树序列,请求出这棵树中最多和最少有多少个点能够被染成绿色。
对于全部的测试点,保证 , 中只含字符 0 1 2。
思路
由于有三种颜色,我们可以把它建模成 ,顺序其实无所谓,这里用 当绿了。这个数组也就是在 这个结点颜色为绿/红/蓝的三种颜色的绿色结点最大/最小值。
由于不能跟子节点一样,所以自己当前的颜色 就要从不是 的另外两个数过来。特别的,如果这个颜色为绿,就要 。
所以就差不多是这样:
建树的部分就递归建树就好, 和 可以用两个函数解决,第二次的时候清空就行了。之后由于根可以有三个颜色,答案应该在三个颜色的其中。这个看起来更好理解,其实用绿和红/蓝就行。
代码
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;
const ll MAXN=5e5+5;
ll dp[MAXN][4],sz;
char a[MAXN];
struct node{
ll l,r;
}t[MAXN];
ll build(){
ll u=++sz;
if(a[sz]=='2'){
t[u].l=build();
t[u].r=build();
}else if(a[sz]=='1'){
t[u].l=build();
}
return u;
}
void dp1(ll u){
if(t[u].l==0&&t[u].r==0){
dp[u][0]=1;
return;
}else if(t[u].l&&t[u].r){
dp1(t[u].l);
dp1(t[u].r);
}else{
dp1(t[u].l);
}
dp[u][0]= max(dp[t[u].l][1]+dp[t[u].r][2],dp[t[u].l][2]+dp[t[u].r][1])+1;
dp[u][1]= max(dp[t[u].l][2]+dp[t[u].r][0],dp[t[u].l][0]+dp[t[u].r][2]);
dp[u][2]= max(dp[t[u].l][0]+dp[t[u].r][1],dp[t[u].l][1]+dp[t[u].r][0]);
}
void dp2(ll u){
if(t[u].l==0&&t[u].r==0){
dp[u][0]=1;
return;
}else if(t[u].l&&t[u].r){
dp2(t[u].l);
dp2(t[u].r);
}else{
dp2(t[u].l);
}
dp[u][0]= min(dp[t[u].l][1]+dp[t[u].r][2],dp[t[u].l][2]+dp[t[u].r][1])+1;
dp[u][1]= min(dp[t[u].l][2]+dp[t[u].r][0],dp[t[u].l][0]+dp[t[u].r][2]);
dp[u][2]= min(dp[t[u].l][0]+dp[t[u].r][1],dp[t[u].l][1]+dp[t[u].r][0]);
}
int main(){
scanf("%s",(a+1));
build();
dp1(1);
printf("%lld ", max({dp[1][0],dp[1][1],dp[1][2]}));
memset(dp,0,sizeof(dp));
dp2(1);
printf("%lld ", min({dp[1][0],dp[1][1],dp[1][2]}));
return 0;
}
总结
这个问题可以归纳为树上多情况dp,也就是每一个位置的状态不止有一个,而是有多个。比如这题是红蓝绿。