前言
典型的树上dp。
题目大意
你的任务是要对一棵二叉树的节点进行染色。每个节点可以被染成红色、绿色或蓝色。并且,一个节点与其子节点的颜色必须不同,如果该节点有两个子节点,那么这两个子节点的颜色也必须不同。给定一颗二叉树的二叉树序列,请求出这棵树中最多和最少有多少个点能够被染成绿色。
对于全部的测试点,保证
1
≤
∣
s
∣
≤
5
×
1
0
5
1 \leq |s| \leq 5 \times 10^5
1≤∣s∣≤5×105,
s
s
s 中只含字符 0
1
2
。
思路
由于有三种颜色,我们可以把它建模成 d p [ u ] [ 0 / 1 / 2 ] dp[u][0/1/2] dp[u][0/1/2],顺序其实无所谓,这里用 0 0 0 当绿了。这个数组也就是在 u u u 这个结点颜色为绿/红/蓝的三种颜色的绿色结点最大/最小值。
由于不能跟子节点一样,所以自己当前的颜色
d
p
[
u
]
[
k
]
dp[u][k]
dp[u][k] 就要从不是
k
k
k 的另外两个数过来。特别的,如果这个颜色为绿,就要
+
1
+1
+1。
所以就差不多是这样:
d
p
[
u
]
[
0
]
=
m
a
x
(
d
p
[
t
[
u
]
.
l
]
[
1
]
+
d
p
[
t
[
u
]
.
r
]
[
2
]
,
d
p
[
t
[
u
]
.
l
]
[
2
]
+
d
p
[
t
[
u
]
.
r
]
[
1
]
)
+
1
;
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][0]=max(dp[t[u].l][1]+dp[t[u].r][2],dp[t[u].l][2]+dp[t[u].r][1])+1;
d
p
[
u
]
[
1
]
=
m
a
x
(
d
p
[
t
[
u
]
.
l
]
[
2
]
+
d
p
[
t
[
u
]
.
r
]
[
0
]
,
d
p
[
t
[
u
]
.
l
]
[
0
]
+
d
p
[
t
[
u
]
.
r
]
[
2
]
)
;
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][1]=max(dp[t[u].l][2]+dp[t[u].r][0],dp[t[u].l][0]+dp[t[u].r][2]);
d
p
[
u
]
[
2
]
=
m
a
x
(
d
p
[
t
[
u
]
.
l
]
[
0
]
+
d
p
[
t
[
u
]
.
r
]
[
1
]
,
d
p
[
t
[
u
]
.
l
]
[
1
]
+
d
p
[
t
[
u
]
.
r
]
[
0
]
)
;
dp[u][2]= max(dp[t[u].l][0]+dp[t[u].r][1],dp[t[u].l][1]+dp[t[u].r][0]);
dp[u][2]=max(dp[t[u].l][0]+dp[t[u].r][1],dp[t[u].l][1]+dp[t[u].r][0]);
建树的部分就递归建树就好, max \max max 和 min \min min 可以用两个函数解决,第二次的时候清空就行了。之后由于根可以有三个颜色,答案应该在三个颜色的其中。这个看起来更好理解,其实用绿和红/蓝就行。
代码
#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,也就是每一个位置的状态不止有一个,而是有多个。比如这题是红蓝绿。