题目描述
一棵二叉树可以按照如下规则表示成一个由0、1、2组成的字符序列,我们称之为“二叉树序列S”:
0 该树没有子节点 1S1 该树有一个子节点,S1为其二叉树序列 1S1S2 该树有两个子节点,S1,S2分别为两个二叉树的序列 例如,下图所表示的二叉树可以用二叉树序列S=21200110来表示。
你的任务是要对一棵二叉树的节点进行染色。每个节点可以被染成红色、绿色或蓝色。并且,一个节点与其子节点的颜色必须不同,如果该节点有两个子节点,那么这两个子节点的颜色也必须不相同。给定一棵二叉树的二叉树序列,请求出这棵树中最多和最少有多少个点能够被染成绿色。
输入格式
输入文件仅有一行,不超过10000个字符,表示一个二叉树序列
输出格式
输出文件也只有一行,包含两个数,依次表示最多和最少有多少个点能够被染成绿色。
样例
样例输入
1122002010
样例输出
5 2
题目分析
讲真第一眼看见这道题想起了刚学的排列组合emmm
- 这题看起来花里胡哨,其实搞懂题以后,还是比较暴力的
- 关键点:
- 如何建树:根据题意,子串的每一个字符都代表着分支,重点在分支为0这里,分支为0说明走到了根节点,直接返回即可
- 转移方程:每个点都可以涂三种颜色,每种颜色都试一下,加上子树的dp值,而且要分开保存,最大值最小值要分开,不同的颜色也要分开,最后统一比较即可
#include <cstdio>
#include <iostream>
#include <cmath>
#include <algorithm>
#include <cstring>
using namespace std;
const int maxn =1e4+10;
char s[maxn];
int f1[maxn][3],f2[maxn][3];//f1是最大值,f2是最小值,再多开一维数组记录所涂颜色
int dfs(int root){ //返回每个子树跑dfs的末尾元素
if(s[root] == '0'){
f1[root][0] = f2[root][0] = 1;
return root;
}
int left = dfs(root+1);
if(s[root] == '1'){//根据颜色的互异性,直接分情况讨论
f1[root][0] = max(f1[root+1][1],f1[root+1][2])+1;
f1[root][1] = max(f1[root+1][0],f1[root+1][2]);
f1[root][2] = max(f1[root+1][0],f1[root+1][1]);
f2[root][0] = min(f2[root+1][1],f2[root+1][2])+1;
f2[root][1] = min(f2[root+1][0],f2[root+1][2]);
f2[root][2] = min(f2[root+1][0],f2[root+1][1]);
return left;
}
if(s[root] == '2'){//有两个子树,还需要从左子树的末尾接着跑,找出右子树
int right = dfs(left+1);
f1[root][0] = max(f1[root+1][1]+f1[left+1][2],f1[root+1][2]+f1[left+1][1])+1;
f1[root][1] = max(f1[root+1][0]+f1[left+1][2],f1[root+1][2]+f1[left+1][0]);
f1[root][2] = max(f1[root+1][0]+f1[left+1][1],f1[root+1][1]+f1[left+1][0]);
f2[root][0] = min(f2[root+1][1]+f2[left+1][2],f2[root+1][2]+f2[left+1][1])+1;
f2[root][1] = min(f2[root+1][0]+f2[left+1][2],f2[root+1][2]+f2[left+1][0]);
f2[root][2] = min(f2[root+1][0]+f2[left+1][1],f2[root+1][1]+f2[left+1][0]);
return right;
}
}
int main(){
cin>>s;
dfs(0);
int MAX = max(f1[0][0],max(f1[0][1],f1[0][2]));//三种情况进行比较,得出最大值
int MIN = min(f2[0][0],min(f2[0][1],f2[0][2]));//得出最小值
printf("%d %d",MAX,MIN);
}