P2585 [ZJOI2006]三色二叉树 树形Dp
输入格式
输入文件仅有一行,不超过10000个字符,表示一个二叉树序列
输出格式
输出文件也只有一行,包含两个数,依次表示最多和最少有多少个点能够被染成绿色。
样例
样例输入
1122002010
样例输出
5 2
可以用f[i][0],f[i][1],f[i][2]分别表示当i被染成绿色,红色,蓝色时,以i为根节点的子树中最多能被染成绿色的点数
(注意不要误认为是各颜色的最多点数,是最多绿色点数)
ff来表示最少点数。
之后就按照题目描述去模拟了。
(最开始我打算先用结构体建树,再dfs,但直接邻接矩阵dfs更方便(毕竟塔是个二叉树
目标:ans=max(f[1][x],x=0或1或2)和 anss=min(ff[1][x],x=0或1或2).
(有些坑标注在代码里面了...每一个栽进去都是满满的疼啊...
#include <cstdio> #include <cmath> #include <cstring> #include <algorithm> using namespace std; const int MAXN = 1e4+10; typedef long long ll; int f[MAXN][4],ff[MAXN][4],now,ans=1,anss; char a[MAXN]; void dfs(int i){//i为节点编号 if(a[i]=='0'){//i为叶子结点,以叶子结点为子树的树只有它自己 f[i][0]=ff[i][0]=1; return;//i为绿色,则最多最少都是1 } dfs(++now);//dfs左儿子(左儿子编号一定为i+1,也就是now+1),为下面求i的函数值做铺垫 if(a[i]=='1'){//如果i只有一个儿子 f[i][0]=max(f[i+1][1],f[i+1][2])+1;//当i为绿色,点数+1 f[i][1]=max(f[i+1][0],f[i+1][2]);//当i非绿,点数不加 f[i][2]=max(f[i+1][0],f[i+1][1]); //同理 ff[i][0]=min(ff[i+1][1],ff[i+1][2])+1;//当i为绿色,点数+1 ff[i][1]=min(ff[i+1][0],ff[i+1][2]);//当i非绿,点数不加 ff[i][2]=min(ff[i+1][0],ff[i+1][1]); }else{//i有两个儿子 int k = ++now;//右儿子肯定不和父亲相邻,now此时为上一次dfs左儿子时得到的最后一个叶子结点,++now就到了右儿子上 dfs(k);//这里不可直接dfs(++now),因为在dfs右儿子的过程中now会被更改.而我们下面是要用原来右儿子的值去改i的值,所以要用k记录下右儿子编号 f[i][0] =max(f[i+1][1]+f[k][2],f[i+1][2]+f[k][1])+1;//分情况讨论 f[i][1]=max(f[i+1][0]+f[k][2],f[i+1][2]+f[k][0]); f[i][2]=max(f[i+1][0]+f[k][1],f[i+1][1]+f[k][0]); ff[i][0]=min(ff[i+1][1]+ff[k][2],ff[i+1][2]+ff[k][1])+1; ff[i][1]=min(ff[i+1][0]+ff[k][2],ff[i+1][2]+ff[k][0]); ff[i][2]=min(ff[i+1][0]+ff[k][1],ff[i+1][1]+ff[k][0]); } //ans=max(ans,f[i][0]); } int main(){ scanf("%s",a+1); dfs(++now); ans=max(max(f[1][0],f[1][1]),f[1][2]); anss=min(min(ff[1][0],ff[1][1]),ff[1][2]); printf("%d %d",ans,anss); return 0; }