[ZJOI2006] 三色二叉树
树形dp
题目大意:给定一棵二叉树,将节点染成红、绿,蓝三种颜色,求绿色节点个数的最大值和最小值。
这题建树十分恶心,需要一些技巧:
观察输入数列及题面,可以知道任意一个节点的左子树一定在右子树的左边,且紧邻右子树
这样就可以跑一个dfs,先搜左子树,记录左子树节点个数ln,则数组下标ln+1的数便是右子树的根,再搜右子树,记录节点个数rn,则ln+rn+1就是下一个要搜的点
对于染色,每个节点可以分为两种情况:染绿色和不染绿色
我们可以令f[i][0/1]表示以节点i为根的子树中,当i不染绿色/染绿色时,绿色节点个数的最大值,ff[i][0/1]同理,求最小值
则当i有一个儿子时,f[i][1]=f[ls[i]][0]+1
则当i有两个儿子时,f[i][0]=max(f[ls[i]][0]+f[rs[i]][1],f[rs[i]][0]+f[ls[i]][1]) f[i][1]=f[ls[i]][0]+f[rs[i]][0]+1
求最小值同理
代码:
#include<bits/stdc++.h> using namespace std; char c[1000005]; int p[1000005]; int n; int lst; int s[1000005]; int f[1000005][2]; int ff[1000005][2]; int ls[1000005],rs[1000005]; int build(int cur) { if(p[cur]==0) return 1; else if(p[cur]==1) { ls[cur]=cur+1; int t=build(cur+1); return t+1; } else { ls[cur]=cur+1; int t=build(cur+1); rs[cur]=cur+t+1; int tt=build(cur+t+1); return t+tt+1; } } void dfs(int i) { if(ls[i]!=0) dfs(ls[i]); if(rs[i]!=0) dfs(rs[i]); if(s[i]==1) { f[i][1]=f[ls[i]][0]+1; ff[i][1]=ff[ls[i]][0]+1; f[i][0]=max(f[ls[i]][0],f[ls[i]][1]); ff[i][0]=min(ff[ls[i]][0],ff[ls[i]][1]); } if(s[i]==2) { f[i][1]=f[ls[i]][0]+f[rs[i]][0]+1; ff[i][1]=ff[ls[i]][0]+ff[rs[i]][0]+1; f[i][0]=max(f[ls[i]][0]+f[rs[i]][1],f[rs[i]][0]+f[ls[i]][1]); ff[i][0]=min(ff[ls[i]][0]+ff[rs[i]][1],ff[rs[i]][0]+ff[ls[i]][1]); } } int main() { cin>>(c+1); n=strlen(c+1); for(int i=1;i<=n;i++) p[i]=c[i]-'0'; build(1); for(int i=1;i<=n;i++) { if(ls[i]&&rs[i]) s[i]=2; else if(!ls[i]&&!rs[i]) s[i]=0; else s[i]=1; if(s[i]==0) f[i][1]=ff[i][1]=1; } dfs(1); //for(int i=1;i<=n;i++) cout<<i<<' '<<f[i][0]<<' '<<f[i][1]<<endl; cout<<max(f[1][0],f[1][1])<<' '<<min(ff[1][1],ff[1][0])<<endl; return 0; }
谷歌输入法真™难用