红豆生南国
有诗云:
相思 (王维 唐) 红豆生南国, 春来发几枝。 愿君多采撷, 此物最相思。
那么,我们来采红豆吧!
假设红豆树是这个样子的:
这种红豆树的特点是:
- 每个结点都有一个正整数编号,标在结点内部。结点的编号各不相同。
- 最上方一层结点是 “
红豆
”(图中红圈所示的5个结点),这一层被称之为红豆层。- 树的根结点、左子结点、右子结点、左子树、右子树等的定义与“数据结构”中的“二叉树”相同,但它毕竟是“自然界中的树”,树根在最下方,如图中的
结点5
- 图中这棵红豆树是“完全二叉红豆树”,类似“数据结构”中的“完全二叉树”。(“完全二叉树”的定义:一个二叉树,如果每一个层的结点数都达到最大值,则这个二叉树就是完美二叉树。对于一个有N个结点的二叉树,若其结点对应于相同深度完美二叉树的层序遍历的前 N 个结点,这样的树就是完全二叉树) 从图上看,就是:要么每一层(包括红豆层)的结点数达到最大值,要么只在红豆层的最右边缺少一些结点。
对于红豆树,我们定义两种遍历顺序:
正序遍历
:先访问树根结点,再正序遍历
其左子树,最后正序遍历
其右子树逆序遍历
:先逆序遍历
其右子树,再逆序遍历
其左子树,最后访问树根结点对于给定的一棵
完全二叉红豆树
以及一些要采撷的结点,计算每次采撷能采到的红豆数量。注意:我们采的点,可能是红豆,也可能不是红豆。采撷一个结点的意思是,把这个结点及这个结点的子树的全部结点从树中采下来。
例如:若采结点7,这是红豆结点,我们将获得1颗红豆;若采结点11,这不是红豆结点(而是一个枝结点!),我们将获得红豆树的一枝,包含2个红豆结点(8和2)。
输入格式:
输入有四行。
第一行是一个不超过
60
的正整数N
,表示完全二叉红豆树中的结点数量。第二行是
N
个不超过1000
的结点编号序列,以空格间隔,表示的是这棵树的逆序遍历
序列。第三行是一个不超过
N
的正整数K
,表示进行K
次采撷。第四行是
K
个正整数,依次表示每次要采的结点编号。输出格式:
输出包含
K+1
行,前K行,对于输入的每个采撷的点,在一行输出相应获得的红豆数量。如果这个点已经被采掉了,则输出
Zao Jiu Cai Diao Le!
。如果这个点在原树中根本不存在,则输出Kan Qing Chu Le?
。最后一行,输出采撷结束之后,这棵红豆树的
正序遍历
序列,用空格分隔,最后一个结点之后没有空格。如果采撷结束之后树已空,则输出Kong Le!
输入样例1:
对于题目中给出的图,对应的输入是:
12 10 4 3 12 6 7 1 2 8 11 9 5 4 15 12 11 2
输出样例1:
Kan Qing Chu Le? 1 2 Zao Jiu Cai Diao Le! 5 9 1 7 6
输入样例2:
对于题目中给出的图,对应的输入是:
12 10 4 3 12 6 7 1 2 8 11 9 5 1 5
输出样例2:
5 Kong Le!
思路:根据他的后序结果和完全二叉树性质,可以建树,然后就是二叉树模板
坑点:1.判断一颗子树是否被采摘过不能直接以他的果子树为0来判断,而是要判断他有没有被摘过,比如我摘图中的10号点,他虽然果子树为0,但是他没被摘过,所以输出的是00而不是”被摘过了“;
2.注意建树需要先建右子树。
3.只有最后一层的节点是果子,其他节点都不算。
4.输入的是节点的值而不是序号。
#include<bits/stdc++.h>
using namespace std;
int n,m,k;
int va[10] ={0,1,3,7,15,31,63,127};//手搓一个数组,分别对应n层的完全二叉树有多少节点
int is_guo[10024];//用来标记这个节点是否是果子
int tree[10086];//因为节点数量最多只有60个,也就是6层,所以直接采用数组存树
vector<int>ans;//用来存储最后前序遍历的结果,方便使用规定的格式输出
map<int,int>mp;//标记编号n节点在树上的真实序列号是什么,相当于输入的n是节点的名称,和实际的节点编号有可能是不一致的
void build(int x){//这里采用递归建树,递归到最后一个节点,然后获取输入,保存到树上
if(x<=n){
build(2*x+1);//根据题意,先建右子树
build(2*x);//再建左树
cin>>tree[x];
mp[tree[x]]=x;//建立映射,方便获取真实的节点编号
}
}
int zhai(int x){//递归摘取果子
int res = 0;//统计有多少个果子
if(x<=n && tree[x]){
if(is_guo[x])res++;//如果是果子就++
tree[x]=0;//摘过之后把节点置0,防止后面重复遍历
res += zhai(2*x);//继续摘下一层,
res += zhai(2*x+1);
}
return res;
}
void qian(int x){//前序遍历树模板
if(x<=n && tree[x]){
ans.push_back(tree[x]);//把答案保存到vector方便输出
qian(2*x);
qian(2*x+1);
}
}
int main(){
memset(is_guo,0,sizeof is_guo);
cin>>n;
build(1);//建树
int ceng = 1;
for(int i=1;i<=7;i++){//这里用个循环求出n个节点应该有多少层
if(n<=va[i]){
ceng = i;
break;
}
}
for(int i=va[ceng-1]+1;i<=n;i++){
is_guo[i]=1; //标记最后一层的叶子节点是果子
}
cin>>k;
while(k--){
int x;
cin>>x;
if(!mp[x]){//如果编号是没在树中出现过的,那直接输出
cout<<"Kan Qing Chu Le?"<<endl;
}else if(tree[mp[x]]==0){//这里要注意,判断树有没有被摘过不能直接判这次摘到的果子是否为0,因为我如果摘的子树没有果子,应该是出0
//因为摘过的果子会被递归成0,所以直接判是否为0;
cout<<"Zao Jiu Cai Diao Le!"<<endl;
}else{//否则就是没摘过,直接调用zhai函数
cout<<zhai(mp[x])<<endl;
}
}
if(!tree[1]){//如果根节点也是0,那说明树空了, 否则必定还有残留节点
cout<<"Kong Le!"<<endl;
}else{
qian(1);//从根节点开始遍历
int f =0;
for(auto it:ans){
if(f!=0)cout<<" ";
f =1;
cout<<it;
}
if(f!=0)cout<<endl;
}
return 0;
}