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;
}
 
原文地址:https://www.cnblogs.com/Siegfried-L/p/12986877.html