一棵二叉树可以按照如下规则表示成一个由 0 0 0、 1 1 1、 2 2 2 组成的字符序列,称之为“二叉树序列 S S S”:
S = { 0 表示该树没有子节点 1 S 1 表示该树有一个节点, S 1 为其子树的二叉树序列 2 S 1 S 2 表示该树有两个子节点, S 1 和 S 2 分别表示其两个子树的二叉树序列 S= \begin{cases} 0& \text表示该树没有子节点\\ 1S_1& 表示该树有一个节点,S_1 为其子树的二叉树序列\\ 2S_1S_2& 表示该树有两个子节点,S_1 和 S_2 分别表示其两个子树的二叉树序列 \end{cases} S=⎩ ⎨ ⎧01S12S1S2表示该树没有子节点表示该树有一个节点,S1为其子树的二叉树序列表示该树有两个子节点,S1和S2分别表示其两个子树的二叉树序列
任务是要对一棵二叉树的节点进行染色。每个节点可以被染成红色、绿色或蓝色。并且,一个节点与其子节点的颜色必须不同,如果该节点有两个子节点,那么这两个子节点的颜色也必须不同。给定一颗二叉树的二叉树序列,请求出这棵树中最多和最少有多少个点能够被染成绿色。
设状态为
f
(
u
,
0
/
1
/
2
)
f(u,0/1/2)
f(u,0/1/2) ,表示以节点u为根且u的颜色为绿色/红色/蓝色的情况下的一颗子树中最多(最少同理)有多少个节点能被染成绿色。
那么状态转移方程为:
f
(
u
,
0
)
=
{
1
s
=
0
max
(
f
(
v
1
,
1
)
,
f
(
v
1
,
2
)
)
+
1
s
=
1
max
(
f
(
v
1
,
1
)
+
f
(
v
2
,
2
)
,
f
(
v
1
,
2
)
+
f
(
v
2
,
2
)
+
1
s
=
2
f(u,0) = \begin{cases} 1 & s = 0\\ \max(f({v_1},1),f({v_1},2)) + 1 & s= 1\\ \max(f({v_1},1) + f({v_2},2),f({v_1},2) + f({v_2},2) + 1 & s = 2\\ \end{cases}
f(u,0)=⎩
⎨
⎧1max(f(v1,1),f(v1,2))+1max(f(v1,1)+f(v2,2),f(v1,2)+f(v2,2)+1s=0s=1s=2
f
(
u
,
1
)
=
{
max
(
f
(
v
1
,
0
)
,
f
(
v
1
,
2
)
)
s
=
1
max
(
f
(
v
1
,
0
)
+
f
(
v
2
,
2
)
,
f
(
v
1
,
2
)
+
f
(
v
2
,
0
)
s
=
2
f(u,1) = \begin{cases} \max(f({v_1},0),f({v_1},2)) & s= 1\\ \max(f({v_1},0) + f({v_2},2),f({v_1},2) + f({v_2},0) & s = 2\\ \end{cases}
f(u,1)={max(f(v1,0),f(v1,2))max(f(v1,0)+f(v2,2),f(v1,2)+f(v2,0)s=1s=2
f
(
u
,
2
)
=
{
max
(
f
(
v
1
,
0
)
,
f
(
v
1
,
1
)
)
s
=
1
max
(
f
(
v
1
,
0
)
+
f
(
v
2
,
1
)
,
f
(
v
1
,
1
)
+
f
(
v
2
,
0
)
s
=
2
f(u,2) = \begin{cases} \max(f({v_1},0),f({v_1},1)) & s= 1\\ \max(f({v_1},0) + f({v_2},1),f({v_1},1) + f({v_2},0) & s = 2\\ \end{cases}
f(u,2)={max(f(v1,0),f(v1,1))max(f(v1,0)+f(v2,1),f(v1,1)+f(v2,0)s=1s=2
其中
v
1
v_1
v1 、
v
2
v_2
v2 分别表示节点u的两个子节点,
s
s
s 表示节点u的子节点个数(最小值同理)。
最后选出以0号节点也就是根节点为根的树中3种不同颜色下的最大值和最小值即可。大致过程如下:

完整代码:
#include <bits/stdc++.h>
using namespace std;
const int N = 500010;
string str;
vector<int> e[N];
int dp_max[N][3],dp_min[N][3];
int build(int p,int f){
if(f != -1)
e[f].push_back(p);
if(str[p] == '0')
return p;
if(str[p] == '1')
return build(p + 1,p);
if(str[p] == '2')
return build(build(p + 1,p) + 1,p);
return -1;
}
void dfs(int u){
if(str[u] == '0'){
dp_max[u][0] = dp_min[u][0] = 1;
return;
}
for(int v : e[u])
dfs(v);
if(str[u] == '1'){
dp_max[u][0] = max(dp_max[e[u][0]][1],dp_max[e[u][0]][2]) + 1;
dp_max[u][1] = max(dp_max[e[u][0]][0],dp_max[e[u][0]][2]);
dp_max[u][2] = max(dp_max[e[u][0]][0],dp_max[e[u][0]][1]);
dp_min[u][0] = min(dp_min[e[u][0]][1],dp_min[e[u][0]][2]) + 1;
dp_min[u][1] = min(dp_min[e[u][0]][0],dp_min[e[u][0]][2]);
dp_min[u][2] = min(dp_max[e[u][0]][0],dp_min[e[u][0]][1]);
}
else{
dp_max[u][0] = max(dp_max[e[u][0]][1] + dp_max[e[u][1]][2],dp_max[e[u][0]][2] + dp_max[e[u][1]][1]) + 1;
dp_max[u][1] = max(dp_max[e[u][0]][0] + dp_max[e[u][1]][2],dp_max[e[u][0]][2] + dp_max[e[u][1]][0]);
dp_max[u][2] = max(dp_max[e[u][0]][0] + dp_max[e[u][1]][1],dp_max[e[u][0]][1] + dp_max[e[u][1]][0]);
dp_min[u][0] = min(dp_min[e[u][0]][1] + dp_min[e[u][1]][2],dp_min[e[u][0]][2] + dp_min[e[u][1]][1]) + 1;
dp_min[u][1] = min(dp_min[e[u][0]][0] + dp_min[e[u][1]][2],dp_min[e[u][0]][2] + dp_min[e[u][1]][0]);
dp_min[u][2] = min(dp_min[e[u][0]][1] + dp_min[e[u][1]][0],dp_min[e[u][0]][0] + dp_min[e[u][1]][1]);
}
}
int main(){
cin >> str;
build(0,-1);
dfs(0);
cout << max(dp_max[0][0],max(dp_max[0][1],dp_max[0][2])) << " " << min(dp_min[0][0],min(dp_min[0][1],dp_min[0][2])) << endl;
return 0;
}
3万+

被折叠的 条评论
为什么被折叠?



