P2585 [ZJOI2006]三色二叉树


前言

典型的树上dp。


题目大意

你的任务是要对一棵二叉树的节点进行染色。每个节点可以被染成红色、绿色或蓝色。并且,一个节点与其子节点的颜色必须不同,如果该节点有两个子节点,那么这两个子节点的颜色也必须不同。给定一颗二叉树的二叉树序列,请求出这棵树中最多和最少有多少个点能够被染成绿色。

对于全部的测试点,保证 1 ≤ ∣ s ∣ ≤ 5 × 1 0 5 1 \leq |s| \leq 5 \times 10^5 1s5×105 s s s 中只含字符 0 1 2


思路

由于有三种颜色,我们可以把它建模成 d p [ u ] [ 0 / 1 / 2 ] dp[u][0/1/2] dp[u][0/1/2],顺序其实无所谓,这里用 0 0 0 当绿了。这个数组也就是在 u u u 这个结点颜色为绿/红/蓝的三种颜色的绿色结点最大/最小值。

由于不能跟子节点一样,所以自己当前的颜色 d p [ u ] [ k ] dp[u][k] dp[u][k] 就要从不是 k k k 的另外两个数过来。特别的,如果这个颜色为绿,就要 + 1 +1 +1
所以就差不多是这样:
d p [ u ] [ 0 ] = m a x ( d p [ t [ u ] . l ] [ 1 ] + d p [ t [ u ] . r ] [ 2 ] , d p [ t [ u ] . l ] [ 2 ] + d p [ t [ u ] . r ] [ 1 ] ) + 1 ; dp[u][0]= max(dp[t[u].l][1]+dp[t[u].r][2],dp[t[u].l][2]+dp[t[u].r][1])+1; dp[u][0]=max(dp[t[u].l][1]+dp[t[u].r][2],dp[t[u].l][2]+dp[t[u].r][1])+1;
d p [ u ] [ 1 ] = m a x ( d p [ t [ u ] . l ] [ 2 ] + d p [ t [ u ] . r ] [ 0 ] , d p [ t [ u ] . l ] [ 0 ] + d p [ t [ u ] . r ] [ 2 ] ) ; dp[u][1]= max(dp[t[u].l][2]+dp[t[u].r][0],dp[t[u].l][0]+dp[t[u].r][2]); dp[u][1]=max(dp[t[u].l][2]+dp[t[u].r][0],dp[t[u].l][0]+dp[t[u].r][2]);
d p [ u ] [ 2 ] = m a x ( d p [ t [ u ] . l ] [ 0 ] + d p [ t [ u ] . r ] [ 1 ] , d p [ t [ u ] . l ] [ 1 ] + d p [ t [ u ] . r ] [ 0 ] ) ; dp[u][2]= max(dp[t[u].l][0]+dp[t[u].r][1],dp[t[u].l][1]+dp[t[u].r][0]); dp[u][2]=max(dp[t[u].l][0]+dp[t[u].r][1],dp[t[u].l][1]+dp[t[u].r][0]);

建树的部分就递归建树就好, max ⁡ \max max min ⁡ \min min 可以用两个函数解决,第二次的时候清空就行了。之后由于根可以有三个颜色,答案应该在三个颜色的其中。这个看起来更好理解,其实用绿和红/蓝就行。


代码

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;
const ll MAXN=5e5+5;
ll dp[MAXN][4],sz;
char a[MAXN];
struct node{
    ll l,r;
}t[MAXN];
ll build(){
    ll u=++sz;
    if(a[sz]=='2'){
        t[u].l=build();
        t[u].r=build();
    }else if(a[sz]=='1'){
        t[u].l=build();
    }
    return u;
}
void dp1(ll u){
    if(t[u].l==0&&t[u].r==0){
        dp[u][0]=1;
        return;
    }else if(t[u].l&&t[u].r){
        dp1(t[u].l);
        dp1(t[u].r);
    }else{
        dp1(t[u].l);
    }
    dp[u][0]= max(dp[t[u].l][1]+dp[t[u].r][2],dp[t[u].l][2]+dp[t[u].r][1])+1;
    dp[u][1]= max(dp[t[u].l][2]+dp[t[u].r][0],dp[t[u].l][0]+dp[t[u].r][2]);
    dp[u][2]= max(dp[t[u].l][0]+dp[t[u].r][1],dp[t[u].l][1]+dp[t[u].r][0]);
}

void dp2(ll u){
    if(t[u].l==0&&t[u].r==0){
        dp[u][0]=1;
        return;
    }else if(t[u].l&&t[u].r){
        dp2(t[u].l);
        dp2(t[u].r);
    }else{
        dp2(t[u].l);
    }
    dp[u][0]= min(dp[t[u].l][1]+dp[t[u].r][2],dp[t[u].l][2]+dp[t[u].r][1])+1;
    dp[u][1]= min(dp[t[u].l][2]+dp[t[u].r][0],dp[t[u].l][0]+dp[t[u].r][2]);
    dp[u][2]= min(dp[t[u].l][0]+dp[t[u].r][1],dp[t[u].l][1]+dp[t[u].r][0]);
}
int main(){
    scanf("%s",(a+1));
    build();
    dp1(1);
    printf("%lld ", max({dp[1][0],dp[1][1],dp[1][2]}));
    memset(dp,0,sizeof(dp));
    dp2(1);
    printf("%lld ", min({dp[1][0],dp[1][1],dp[1][2]}));
    return 0;
}

总结

这个问题可以归纳为树上多情况dp,也就是每一个位置的状态不止有一个,而是有多个。比如这题是红蓝绿。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值