[BZOJ1864][CODEVS2462]三色二叉树
题目描述 Description |
一棵二叉树可以按照如下规则表示成一个由0、1、2组成的字符序列,我们称之为“二叉树序列S”: |-0 表示该树没有子节点 S = |-1S1 表示该树有一个子节点,S1为其子树的二叉树排列 |- 2S1S2 表示该树有连个个子节点,S1、S2为其子树的二叉树排列 你的任务是要对一棵二叉树的节点进行染色。每个节点可以被染成红色、绿色或蓝色。并且,一个节点与其子节点的颜色必须不同,如果该节点有两个子节点,那么这两个子节点的颜色也必须不相同。给定一棵二叉树的二叉树序列,请求出这棵树中最多和最少有多少个点能够被染成绿色。 |
输入描述 Input Description |
输入文件仅有一行,不超过10000个字符,表示一个二叉树序列。 |
输出描述 Output Description |
输出文件也只有一行,包含两个数,依次表示最多和最少有多少个点能够被染成绿色。 |
样例输入 Sample Input |
1122002010 |
样例输出 Sample Output |
5 2 |
数据范围及提示 Data Size & Hint |
之前的一些废话:准备期末复习。
题解:
f(i,0)表示这个点不为绿色
0个儿子:0
1个儿子:max(f(s,1),f(s,0))
2个儿子:max(f(s1,1)+f(s2,0),f(s1,0)+f(s2,2))
f(i,1)表示这个点为绿色
0个儿子:1
一个儿子:f(s,0)+1
两个儿子:f(s1,0)+f(s2,0)+1
g(i,0)表示这个点不为绿色
0个儿子:0
1个儿子:min(g(s,1),g(s,0))
2个儿子:min(g(s1,1)+g(s2,0),g(s1,0)+g(s2,2))
f(i,1)表示这个点为绿色
0个儿子:1
一个儿子:g(s,0)+1
两个儿子:g(s1,0)+g(s2,0)+1
然后把输入转化成一颗树的话。。下面代码已经写了。
代码:
#include<iostream> #include<algorithm> #include<cstdio> #include<queue> #include<cmath> #include<cstring> using namespace std; typedef long long LL; typedef pair<int,int> PII; #define mem(a,b) memset(a,b,sizeof(a)) inline int read() { int x=0,f=1;char c=getchar(); while(!isdigit(c)){if(c=='-')f=-1;c=getchar();} while(isdigit(c)){x=x*10+c-'0';c=getchar();} return x*f; } const int maxn=500010; char s[maxn]; int len,tot=1,f[2][maxn],g[2][maxn],l[maxn],r[maxn]; void DP(int now) { if(tot>len)return; f[1][now]=1;g[1][now]=1; if(s[now]=='0')return; if(s[now]=='1') { tot++;l[now]=tot;DP(tot); f[0][now]+=max(f[0][l[now]],f[1][l[now]]); f[1][now]+=f[0][l[now]]; g[0][now]+=min(g[0][l[now]],g[1][l[now]]); g[1][now]+=g[0][l[now]]; } if(s[now]=='2') { tot++;l[now]=tot;DP(tot); tot++;r[now]=tot;DP(tot); f[0][now]+=max(f[1][l[now]]+f[0][r[now]],f[0][l[now]]+f[1][r[now]]); f[1][now]+=f[0][l[now]]+f[0][r[now]]; g[0][now]+=min(g[1][l[now]]+g[0][r[now]],g[0][l[now]]+g[1][r[now]]); g[1][now]+=g[0][l[now]]+g[0][r[now]]; } } int main() { scanf("%s",s+1); len=strlen(s+1); DP(1); printf("%d %d\n",max(f[0][1],f[1][1]),min(g[0][1],g[0][1])); return 0; }
总结: