[ZJOI2006]三色二叉树

[ZJOI2006]三色二叉树

题意:

给定一个中序遍历的二叉树序列 , 每个值代表:

  • \(0\) 无儿子
  • \(1\) 有左儿子
  • \(2\) 有右儿子

要求染色,一共有三种颜色:绿,蓝,红,儿子不和父亲颜色相同,若有两个儿子节点则两个儿子节点颜色也不相同,求一种颜色染色个数的最大值和最小值。

分析:

先建树,因为知道是中序遍历,我们可以先建左儿子,再建右儿子。

因为不好传递是建立左儿子还是右儿子,因此我们可以利用指针,直接给左儿子和右儿子赋值。

void dfs(int &x){
    x=++tot;//建树,因为不好确定左儿子和右儿子,直接建即可
    int opt=s[tot]-'0';
    if(opt==0) return;
    if(opt==1) dfs(son[x][0]);
    if(opt==2) dfs(son[x][0]),dfs(son[x][1]);
}

看这种题目:根节点不和子树相同。

是否想起了 没有上司的舞会 ? 这类题目都是这样:

\(dp[x][1]\) 表示 \(x\) 取绿色,则儿子必须取其他颜色,则有:

dp[i][1]=dp[son[i][0]][0]+dp[son[i][1]][0]+1;

\(dp[x][0]\) 表示 \(x\) 不取绿色,则只能有一个儿子取绿色。

因此,最多情况取 \(max\) ,最少情况取 \(min\) 即可

dp[i][0]=max(dp[son[i][0]][1]+dp[son[i][1]][0],dp[son[i][0]][0]+dp[son[i][1]][1]);

dp[i][0]=min(dp[son[i][0]][1]+dp[son[i][1]][0],dp[son[i][0]][0]+dp[son[i][1]][1]);

那么怎么优化 \(dp\) 转移时的复杂度呢?

在建树时,是根据 \(\rightarrow\) 左儿子 \(\rightarrow\) 右儿子 建立的。

而且儿子节点的编号比父亲节点要大。

因此,我们倒序枚举每一个标号所代表的点,依次进行转移即可。

for(int i=n;i>=1;i--){
....    
}

代码:

#include<bits/stdc++.h>
using namespace std;
const int N=5e5+5;

char s[N];
int son[N][2],tot;
int minn=0x3f3f3f3f,maxx;
int dp[N][2],root=0;

void dfs(int &x){
    x=++tot;//建树,因为不好确定左儿子和右儿子,直接建即可
    int opt=s[tot]-'0';
    if(opt==0) return;
    if(opt==1) dfs(son[x][0]);
    if(opt==2) dfs(son[x][0]),dfs(son[x][1]);
}

int main(){
    scanf("%s",s+1);
    dfs(root); int n=strlen(s+1);
    for(int i=n;i>=1;i--){//因为dp顺序是:右儿子,左儿子,中间,因此肯定不会重复计算之类的
        dp[i][1]=dp[son[i][0]][0]+dp[son[i][1]][0]+1;
        dp[i][0]=max(dp[son[i][0]][1]+dp[son[i][1]][0],dp[son[i][0]][0]+dp[son[i][1]][1]);
    }
    cout<<max(dp[1][1],dp[1][0])<<" ";

    for(int i=n;i>=1;i--){
        dp[i][1]=dp[son[i][0]][0]+dp[son[i][1]][0]+1;
        dp[i][0]=min(dp[son[i][0]][1]+dp[son[i][1]][0],dp[son[i][0]][0]+dp[son[i][1]][1]);
    } 
    cout<<min(dp[1][1],dp[1][0])<<endl;
    system("pause");
    return 0;
}
posted @ 2021-10-26 10:33  Evitagen  阅读(87)  评论(0编辑  收藏  举报