• 洛谷P2585&ZJOI 2006-三色二叉树(树的染色-树形DP)


    题目链接:https://www.luogu.com.cn/problem/P2585
    CSDN食用链接:https://blog.csdn.net/qq_43906000/article/details/107965283

    题目描述

    一棵二叉树可以按照如下规则表示成一个由0,1,2组成的字符序列,我们称之为“二叉树序列S”:
    (S=left{egin{matrix} 0 & 表示该树没有子节点\ 1S_1 & 表示该树有一个子节点,S_1为其子树的二叉树序列\ 2S_1S_2 &表示该树由两个子节点,S_1和S_2分别表示其两个子树的二叉树序列 end{matrix} ight.)
    例如,下图所表示的二叉树序列(S=21200110)来表示。
    在这里插入图片描述
    你的任务是要对一棵二叉树的节点进行染色。每个节点可以被染成红色、绿色或蓝色。并且,一个节点与其子节点的颜色必须不同,如果该节点有两个子节点,那么这两个子节点的颜色也必须不同。给定一颗二叉树的二叉树序列,请求出这棵树中最多和最少有多少个点能够被染成绿色。

    输入格式
    输入只有一行一个字符串 ss,表示二叉树序列。

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

    输入
    1122002010
    输出
    5 2

    说明/提示
    数据规模与约定

    对于全部的测试点,保证(1leq |s|leq5 imes 10^5),s只含字符0,1,2。

    emmm,题难到不难,就是输入的处理恶心了点。。我们可以递归建一下树,可参考如下代码:

    int dfs_wedge(char *s,int fa)
    {
        if (!s[0]) return 0;
        int nb=1;
        cnt++;
        if (fa!=-1){
            g[cnt].push_back(fa);
            g[fa].push_back(cnt);
        }
        if (s[0]=='0') return nb;
        int u=cnt;
        nb+=dfs_wedge(s+1,u);
        if (s[0]=='2') nb+=dfs_wedge(s+nb,u);
        return nb;
    }
    

    那么接下来就是处理染色的问题了,对于最大的情况我们很容易考虑,和之前上司的舞会,最小点覆盖的情况类似,直接记录一下将其染成绿色和不染成绿色所需要的最大值就好了。实际上也就是将当前点染色的时候,所有的儿子不能染色,也就是(dp[u][1]=1+sum dp[v][0]),而当前点不染色的时候,儿子的情况就是要么全都不染色,要么只染其中一个,不能全都染(题目要求的。。。)那么也就是说如果有两个儿子的话就是(dp[u][0]=max(dp[v1][0]+dp[v2][0],max(dp[v1][0]+dp[v2][1],dp[v1][1]+dp[v2][0]))),如果只有一个儿子的话,我们直接取它的最大值就好了:$ dp[u][0]=max(dp[v1][0],dp[v1][1])$那么我们就很容易得出如下代码:

    void dfs(int u,int fa)
    {
        int v1=0,v2=0;
        for (int i=0; i<g[u].size(); i++){
        	int v=g[u][i];
            if (v==fa) continue;
            dfs(v,u);
            dp[u][1]+=dp[v][0];
            if (v1) v2=v;
            else v1=v;
        }
        dp[u][1]++;
        if (v2) {
            dp[u][0]=max(dp[v1][0]+dp[v2][0],max(dp[v1][0]+dp[v2][1],dp[v1][1]+dp[v2][0]));
        }
        else if (v1) dp[u][0]=max(dp[v1][0],dp[v1][1]);
    }
    

    接下来考虑最小的情况。。。这个感觉有点糟。。。。我能全都不染色的吗?显然不行,如果一个节点有两个儿子,这种情况是必须要有绿色的,所以我们在对这种节点不染色的时候要注意,不能两个儿子都没有染,所以也就是必须有一个染色了:(dp[u][0]=min(dp[v1][1]+dp2[v2][0],dp[v1][0]+dp[v2][1]))而如果给当前节点染色。。。那么和max的情况就一样了。

    以下是AC代码:

    #include <bits/stdc++.h>
    using namespace std;
    
    #define debug printf("#@$@%
    ")
    const int mac=5e5+10;
    
    char s[mac];
    vector<int>g[mac];
    int cnt=0,dp[mac][3];
    int dp2[mac][3];
    
    int dfs_wedge(char *s,int fa)
    {
        if (!s[0]) return 0;
        int nb=1;
        cnt++;
        if (fa!=-1){
            g[cnt].push_back(fa);
            g[fa].push_back(cnt);
        }
        if (s[0]=='0') return nb;
        int u=cnt;
        nb+=dfs_wedge(s+1,u);
        if (s[0]=='2') nb+=dfs_wedge(s+nb,u);
        return nb;
    }
    
    void dfs(int u,int fa)
    {
        int v1=0,v2=0;
        for (int i=0; i<g[u].size(); i++){
        	int v=g[u][i];
            if (v==fa) continue;
            dfs(v,u);
            dp[u][1]+=dp[v][0];
            dp2[u][1]+=dp2[v][0];
            if (v1) v2=v;
            else v1=v;
        }
        dp[u][1]++; dp2[u][1]++;
        if (v2) {
            dp2[u][0]=min(dp2[v1][1]+dp2[v2][0],dp2[v1][0]+dp2[v2][1]);
            dp[u][0]=max(dp[v1][0]+dp[v2][0],max(dp[v1][0]+dp[v2][1],dp[v1][1]+dp[v2][0]));
        }
        else if (v1) dp[u][0]=max(dp[v1][0],dp[v1][1]),dp2[u][0]=min(dp2[v1][0],dp2[v1][1]);
    }
    
    int main(int argc, char const *argv[])
    {
        scanf ("%s",s+1);
        if (s[1]=='0') {printf("%d %d
    ",1,0); return 0;}
        dfs_wedge(s+1,-1);
        int n=strlen(s+1);
        dfs(1,-1);
        printf("%d %d
    ",max(dp[1][0],dp[1][1]),min(dp2[1][0],dp2[1][1]));
        return 0;
    }
    
  • 相关阅读:
    jquery加入购物车飞入的效果
    jQuery点击div其他地方隐藏div
    移动对meta的定义
    ZOJ
    博弈dp入门 POJ
    ZOJ 2967计算几何+单调栈
    牛客训练41D最小相似度bfs
    球的体积并
    二进制上的数位dpPOJ 3252
    数位dp入门 HDU 2089 HDU 3555
  • 原文地址:https://www.cnblogs.com/lonely-wind-/p/13492758.html
Copyright © 2020-2023  润新知