[ZJOI2006] 三色二叉树

树形dp

题目传送门

题目大意:给定一棵二叉树,将节点染成红、绿,蓝三种颜色,求绿色节点个数的最大值和最小值。

这题建树十分恶心,需要一些技巧:

观察输入数列及题面,可以知道任意一个节点的左子树一定在右子树的左边,且紧邻右子树

这样就可以跑一个dfs,先搜左子树,记录左子树节点个数ln,则数组下标ln+1的数便是右子树的根,再搜右子树,记录节点个数rn,则ln+rn+1就是下一个要搜的点

 

对于染色,每个节点可以分为两种情况:染绿色和不染绿色

我们可以令f[i][0/1]表示以节点i为根的子树中,当i不染绿色/染绿色时,绿色节点个数的最大值,ff[i][0/1]同理,求最小值

则当i有一个儿子时,f[i][1]=f[ls[i]][0]+1

则当i有两个儿子时,f[i][0]=max(f[ls[i]][0]+f[rs[i]][1],f[rs[i]][0]+f[ls[i]][1])      f[i][1]=f[ls[i]][0]+f[rs[i]][0]+1

求最小值同理

代码:

#include<bits/stdc++.h>
using namespace std;
char c[1000005];
int p[1000005];
int n;
int lst;
int s[1000005];
int f[1000005][2];
int ff[1000005][2];
int ls[1000005],rs[1000005];
int build(int cur)
{
    if(p[cur]==0) return 1;
    else if(p[cur]==1)
    {
        ls[cur]=cur+1;
        int t=build(cur+1);
        return t+1;
    }
    else
    {
        ls[cur]=cur+1;
        int t=build(cur+1);
        rs[cur]=cur+t+1;
        int tt=build(cur+t+1);
        return t+tt+1;
    }
}
void dfs(int i)
{
    if(ls[i]!=0) dfs(ls[i]);
    if(rs[i]!=0) dfs(rs[i]);
    if(s[i]==1)
    {
        f[i][1]=f[ls[i]][0]+1;
        ff[i][1]=ff[ls[i]][0]+1;
        f[i][0]=max(f[ls[i]][0],f[ls[i]][1]);
        ff[i][0]=min(ff[ls[i]][0],ff[ls[i]][1]);
    }
    if(s[i]==2)
    {
        f[i][1]=f[ls[i]][0]+f[rs[i]][0]+1;
        ff[i][1]=ff[ls[i]][0]+ff[rs[i]][0]+1;
        f[i][0]=max(f[ls[i]][0]+f[rs[i]][1],f[rs[i]][0]+f[ls[i]][1]);
        ff[i][0]=min(ff[ls[i]][0]+ff[rs[i]][1],ff[rs[i]][0]+ff[ls[i]][1]);
    }
}
int main()
{
    cin>>(c+1);
    n=strlen(c+1);
    for(int i=1;i<=n;i++) p[i]=c[i]-'0';
    build(1);
    for(int i=1;i<=n;i++)
    {
        if(ls[i]&&rs[i]) s[i]=2;
        else if(!ls[i]&&!rs[i]) s[i]=0;
        else s[i]=1;
        if(s[i]==0) f[i][1]=ff[i][1]=1;
    }
    dfs(1);
    //for(int i=1;i<=n;i++) cout<<i<<' '<<f[i][0]<<' '<<f[i][1]<<endl;
    cout<<max(f[1][0],f[1][1])<<' '<<min(ff[1][1],ff[1][0])<<endl;
    return 0;
}

谷歌输入法真™难用

 

posted @ 2019-10-01 10:00  unsigned_XB  阅读(188)  评论(0编辑  收藏  举报