P2585 (ZJOI2006) 三色二叉树

一棵二叉树可以按照如下规则表示成一个由 0 0 0 1 1 1 2 2 2 组成的字符序列,称之为“二叉树序列 S S S”:
S = { 0 表示该树没有子节点 1 S 1 表示该树有一个节点, S 1 为其子树的二叉树序列 2 S 1 S 2 表示该树有两个子节点, S 1 和 S 2 分别表示其两个子树的二叉树序列 S= \begin{cases} 0& \text表示该树没有子节点\\ 1S_1& 表示该树有一个节点,S_1 为其子树的二叉树序列\\ 2S_1S_2& 表示该树有两个子节点,S_1 和 S_2 分别表示其两个子树的二叉树序列 \end{cases} S= 01S12S1S2示该树没有子节点表示该树有一个节点,S1为其子树的二叉树序列表示该树有两个子节点,S1S2分别表示其两个子树的二叉树序列
任务是要对一棵二叉树的节点进行染色。每个节点可以被染成红色、绿色或蓝色。并且,一个节点与其子节点的颜色必须不同,如果该节点有两个子节点,那么这两个子节点的颜色也必须不同。给定一颗二叉树的二叉树序列,请求出这棵树中最多和最少有多少个点能够被染成绿色。

设状态为 f ( u , 0 / 1 / 2 ) f(u,0/1/2) f(u,0/1/2) ,表示以节点u为根且u的颜色为绿色/红色/蓝色的情况下的一颗子树中最多(最少同理)有多少个节点能被染成绿色。

那么状态转移方程为:
f ( u , 0 ) = { 1 s = 0 max ⁡ ( f ( v 1 , 1 ) , f ( v 1 , 2 ) ) + 1 s = 1 max ⁡ ( f ( v 1 , 1 ) + f ( v 2 , 2 ) , f ( v 1 , 2 ) + f ( v 2 , 2 ) + 1 s = 2 f(u,0) = \begin{cases} 1 & s = 0\\ \max(f({v_1},1),f({v_1},2)) + 1 & s= 1\\ \max(f({v_1},1) + f({v_2},2),f({v_1},2) + f({v_2},2) + 1 & s = 2\\ \end{cases} f(u,0)= 1max(f(v1,1),f(v1,2))+1max(f(v1,1)+f(v2,2),f(v1,2)+f(v2,2)+1s=0s=1s=2
f ( u , 1 ) = { max ⁡ ( f ( v 1 , 0 ) , f ( v 1 , 2 ) ) s = 1 max ⁡ ( f ( v 1 , 0 ) + f ( v 2 , 2 ) , f ( v 1 , 2 ) + f ( v 2 , 0 ) s = 2 f(u,1) = \begin{cases} \max(f({v_1},0),f({v_1},2)) & s= 1\\ \max(f({v_1},0) + f({v_2},2),f({v_1},2) + f({v_2},0) & s = 2\\ \end{cases} f(u,1)={max(f(v1,0),f(v1,2))max(f(v1,0)+f(v2,2),f(v1,2)+f(v2,0)s=1s=2
f ( u , 2 ) = { max ⁡ ( f ( v 1 , 0 ) , f ( v 1 , 1 ) ) s = 1 max ⁡ ( f ( v 1 , 0 ) + f ( v 2 , 1 ) , f ( v 1 , 1 ) + f ( v 2 , 0 ) s = 2 f(u,2) = \begin{cases} \max(f({v_1},0),f({v_1},1)) & s= 1\\ \max(f({v_1},0) + f({v_2},1),f({v_1},1) + f({v_2},0) & s = 2\\ \end{cases} f(u,2)={max(f(v1,0),f(v1,1))max(f(v1,0)+f(v2,1),f(v1,1)+f(v2,0)s=1s=2
其中 v 1 v_1 v1 v 2 v_2 v2 分别表示节点u的两个子节点, s s s 表示节点u的子节点个数(最小值同理)。

最后选出以0号节点也就是根节点为根的树中3种不同颜色下的最大值和最小值即可。大致过程如下:
在这里插入图片描述

完整代码:

#include <bits/stdc++.h>
using namespace std;
const int N = 500010;
string str;
vector<int> e[N];
int dp_max[N][3],dp_min[N][3];

int build(int p,int f){
    if(f != -1)
        e[f].push_back(p);
    if(str[p] == '0')
        return p;
    if(str[p] == '1')
        return build(p + 1,p);
    if(str[p] == '2')
        return build(build(p + 1,p) + 1,p);
    return -1;
}

void dfs(int u){
    if(str[u] == '0'){
        dp_max[u][0] = dp_min[u][0] = 1;
        return;
    }
    for(int v : e[u])
        dfs(v);
    if(str[u] == '1'){
        dp_max[u][0] = max(dp_max[e[u][0]][1],dp_max[e[u][0]][2]) + 1;
        dp_max[u][1] = max(dp_max[e[u][0]][0],dp_max[e[u][0]][2]);
        dp_max[u][2] = max(dp_max[e[u][0]][0],dp_max[e[u][0]][1]);

        dp_min[u][0] = min(dp_min[e[u][0]][1],dp_min[e[u][0]][2]) + 1;
        dp_min[u][1] = min(dp_min[e[u][0]][0],dp_min[e[u][0]][2]);
        dp_min[u][2] = min(dp_max[e[u][0]][0],dp_min[e[u][0]][1]);
    }
    else{
        dp_max[u][0] = max(dp_max[e[u][0]][1] + dp_max[e[u][1]][2],dp_max[e[u][0]][2] + dp_max[e[u][1]][1]) + 1;
        dp_max[u][1] = max(dp_max[e[u][0]][0] + dp_max[e[u][1]][2],dp_max[e[u][0]][2] + dp_max[e[u][1]][0]);
        dp_max[u][2] = max(dp_max[e[u][0]][0] + dp_max[e[u][1]][1],dp_max[e[u][0]][1] + dp_max[e[u][1]][0]);

        dp_min[u][0] = min(dp_min[e[u][0]][1] + dp_min[e[u][1]][2],dp_min[e[u][0]][2] + dp_min[e[u][1]][1]) + 1;
        dp_min[u][1] = min(dp_min[e[u][0]][0] + dp_min[e[u][1]][2],dp_min[e[u][0]][2] + dp_min[e[u][1]][0]);
        dp_min[u][2] = min(dp_min[e[u][0]][1] + dp_min[e[u][1]][0],dp_min[e[u][0]][0] + dp_min[e[u][1]][1]);
    }
}

int main(){
    cin >> str;
    build(0,-1);
    dfs(0);
    cout << max(dp_max[0][0],max(dp_max[0][1],dp_max[0][2])) << " " << min(dp_min[0][0],min(dp_min[0][1],dp_min[0][2])) << endl;

    return 0;
}
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值