bzoj1864 三色二叉树

本文详细解析了如何使用动态规划(DP)算法解决二叉树序列问题,通过构建二叉树并进行DFS遍历,计算以每个节点为根的子树中最多和最少能有多少个特定颜色的节点,适用于算法竞赛和数据结构学习。

Description

Input

仅有一行,不超过500000个字符,表示一个二叉树序列。

Output

输出文件也只有一行,包含两个数,依次表示最多和最少有多少个点能够被染成绿色。

Sample Input

1122002010

Sample Output

5 2
树形dp
int mx[N][3][3]; //点i的颜色为0/1/2,点i为根的子树最多有多少个0/1/2
int mn[N][3][3]; //点i的颜色为0/1/2,点i为根的子树最少有多少个0/1/2
然后各种乱搞
(写的太挫辣。。。直接meta-programming是可以解决的。。。)
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <map>
#include <set>
#include <vector>
#include <queue>
#include <cmath>
#include <cctype>
#include <string>
#include <cfloat>
#include <stack>
#include <cassert>
#include <bitset>

using namespace std;

const int N = 500010;

char str[N];

int ch[N][2], cnt, pos;

int build() {
    if(str[pos] == 0) return 0;
    ++ cnt;
    int cur = cnt;
    int tmp = cnt;
    if(str[pos] == '0') {
        pos ++;
    } else if(str[pos] == '1') {
        pos ++;
        ch[cur][0] = build();
    } else {
        pos ++;
        ch[cur][0] = build();
        ch[cur][1] = build();
    }
    return cur;
}

int mx[N][3][3];   //点i的颜色为0/1/2,点i为根的子树最多有多少个0/1/2

int mn[N][3][3];   //点i的颜色为0/1/2,点i为根的子树最少有多少个0/1/2

void dfs(int u) {
    int c1 = ch[u][0], c2 = ch[u][1];
    if(c1 && c2) {
        //有两个儿子
        dfs(c1);
        dfs(c2);

        mx[u][0][0] = max(mx[c1][1][0] + mx[c2][2][0], mx[c1][2][0] + mx[c2][1][0]) + 1;
        mx[u][0][1] = max(mx[c1][1][1] + mx[c2][2][1], mx[c1][2][1] + mx[c2][1][1]);
        mx[u][0][2] = max(mx[c1][1][2] + mx[c2][2][2], mx[c1][2][2] + mx[c2][1][2]);
        mx[u][1][0] = max(mx[c1][0][0] + mx[c2][2][0], mx[c1][2][0] + mx[c2][0][0]);
        mx[u][1][1] = max(mx[c1][0][1] + mx[c2][2][1], mx[c1][2][1] + mx[c2][0][1]) + 1;
        mx[u][1][2] = max(mx[c1][0][2] + mx[c2][2][2], mx[c1][2][2] + mx[c2][0][2]);
        mx[u][2][0] = max(mx[c1][0][0] + mx[c2][1][0], mx[c1][1][0] + mx[c2][0][0]);
        mx[u][2][1] = max(mx[c1][0][1] + mx[c2][1][1], mx[c1][1][1] + mx[c2][0][1]);
        mx[u][2][2] = max(mx[c1][0][2] + mx[c2][1][2], mx[c1][1][2] + mx[c2][0][2]) + 1;

        mn[u][0][0] = min(mn[c1][1][0] + mn[c2][2][0], mn[c1][2][0] + mn[c2][1][0]) + 1;
        mn[u][0][1] = min(mn[c1][1][1] + mn[c2][2][1], mn[c1][2][1] + mn[c2][1][1]);
        mn[u][0][2] = min(mn[c1][1][2] + mn[c2][2][2], mn[c1][2][2] + mn[c2][1][2]);
        mn[u][1][0] = min(mn[c1][0][0] + mn[c2][2][0], mn[c1][2][0] + mn[c2][0][0]);
        mn[u][1][1] = min(mn[c1][0][1] + mn[c2][2][1], mn[c1][2][1] + mn[c2][0][1]) + 1;
        mn[u][1][2] = min(mn[c1][0][2] + mn[c2][2][2], mn[c1][2][2] + mn[c2][0][2]);
        mn[u][2][0] = min(mn[c1][0][0] + mn[c2][1][0], mn[c1][1][0] + mn[c2][0][0]);
        mn[u][2][1] = min(mn[c1][0][1] + mn[c2][1][1], mn[c1][1][1] + mn[c2][0][1]);
        mn[u][2][2] = min(mn[c1][0][2] + mn[c2][1][2], mn[c1][1][2] + mn[c2][0][2]) + 1;
    } else if(c1) {
        //有一个儿子
        dfs(c1);

        mx[u][0][0] = max(mx[c1][1][0], mx[c1][2][0]) + 1;
        mx[u][0][1] = max(mx[c1][1][1], mx[c1][2][1]);
        mx[u][0][2] = max(mx[c1][1][2], mx[c1][2][2]);
        mx[u][1][0] = max(mx[c1][0][0], mx[c1][2][0]);
        mx[u][1][1] = max(mx[c1][0][1], mx[c1][2][1]) + 1;
        mx[u][1][2] = max(mx[c1][0][2], mx[c1][2][2]);
        mx[u][2][0] = max(mx[c1][0][0], mx[c1][1][0]);
        mx[u][2][1] = max(mx[c1][0][1], mx[c1][1][1]);
        mx[u][2][2] = max(mx[c1][0][2], mx[c1][1][2]) + 1;

        mn[u][0][0] = min(mn[c1][1][0], mn[c1][2][0]) + 1;
        mn[u][0][1] = min(mn[c1][1][1], mn[c1][2][1]);
        mn[u][0][2] = min(mn[c1][1][2], mn[c1][2][2]);
        mn[u][1][0] = min(mn[c1][0][0], mn[c1][2][0]);
        mn[u][1][1] = min(mn[c1][0][1], mn[c1][2][1]) + 1;
        mn[u][1][2] = min(mn[c1][0][2], mn[c1][2][2]);
        mn[u][2][0] = min(mn[c1][0][0], mn[c1][1][0]);
        mn[u][2][1] = min(mn[c1][0][1], mn[c1][1][1]);
        mn[u][2][2] = min(mn[c1][0][2], mn[c1][1][2]) + 1;
    } else {
        //没有儿子
        mx[u][0][0] = 1;
        mx[u][0][1] = 0;
        mx[u][0][2] = 0;
        mx[u][1][0] = 0;
        mx[u][1][1] = 1;
        mx[u][1][2] = 0;
        mx[u][2][0] = 0;
        mx[u][2][1] = 0;
        mx[u][2][2] = 1;

        mn[u][0][0] = 1;
        mn[u][0][1] = 0;
        mn[u][0][2] = 0;
        mn[u][1][0] = 0;
        mn[u][1][1] = 1;
        mn[u][1][2] = 0;
        mn[u][2][0] = 0;
        mn[u][2][1] = 0;
        mn[u][2][2] = 1;
    }
}

int main() {
    scanf("%s", str);
    build();
    dfs(1);
    printf("%d %d\n", 
        max(mx[1][0][0], max(mx[1][1][0], mx[1][2][0])),
        min(mn[1][0][0], min(mn[1][1][0], mn[1][2][0])));
}

  

转载于:https://www.cnblogs.com/KingSann/articles/7507877.html

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值