[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;
}
不关注的有难了😠😠😠https://b23.tv/hoXKV9