题目:

有诗云:

    相思 (王维  唐)红豆生南国, 春来发几枝。愿君多采撷, 此物最相思。

那么,我们来采红豆吧!

假设红豆树是这个样子的:

这种红豆树的特点是:

  • 每个结点都有一个正整数编号,标在结点内部。结点的编号各不相同。
  • 最上方一层结点是 红豆(图中红圈所示的5个结点),这一层被称之为红豆层。
  • 树的根结点、左子结点、右子结点、左子树、右子树等的定义与“数据结构”中的“二叉树”相同,但它毕竟是“自然界中的树”,树根在最下方,如图中的结点5
  • 图中这棵红豆树是“完全二叉红豆树”,类似“数据结构”中的“完全二叉树”。(“完全二叉树”的定义:一个二叉树,如果每一个层的结点数都达到最大值,则这个二叉树就是完美二叉树。对于一个有N个结点的二叉树,若其结点对应于相同深度完美二叉树的层序遍历的前 N 个结点,这样的树就是完全二叉树) 从图上看,就是:要么每一层(包括红豆层)的结点数达到最大值,要么只在红豆层的最右边缺少一些结点。

对于红豆树,我们定义两种遍历顺序:

  1. 正序遍历:先访问树根结点,再正序遍历其左子树,最后正序遍历其右子树
  2. 逆序遍历:先逆序遍历其右子树,再逆序遍历其左子树,最后访问树根结点

对于给定的一棵完全二叉红豆树以及一些要采撷的结点,计算每次采撷能采到的红豆数量。

注意:我们采的点,可能是红豆,也可能不是红豆。采撷一个结点的意思是,把这个结点及这个结点的子树的全部结点从树中采下来。

例如:若采结点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!

题意:

简单来说,就是给定一颗完全二叉树,最后一层的点是果子,然后有m次询问,给定某一个节点编号,需要把这个节点和它对应的子树都砍掉,并输出得到了多少个果子,如果该编号不存在或者已经砍掉了则特判输出即可。

思路:

首先明确我们要解决的问题

  1. 如何根据完全二叉树的"逆序遍历"(可以转化成后序遍历,左右根的顺序遍历),重建这颗二叉树
  2. 如何找到并标记所有的果子
  3. 如何判定给定节点编号是否在树中
  4. 如何判定给定节点编号是否已经被砍掉
  5. 如何模拟摘果子的过程
  6. 如何前序遍历这棵树并输出

首先我们要搞懂,为什么仅根据一次遍历结果就能够重建二叉树,因为这是一颗完全二叉树,它的空节点位置是固定的,都集中在最后一层和倒数第二层,而普通的二叉树却做不到这一点。还有我们上次做的,通过“#”标识空节点,也能够做到一次遍历重建二叉树。一般来说,完全二叉树用一个数组存就行了,和堆的存储一样。然后递归建树即可,注意这里和后序遍历区别在,是先遍历右子树,再是左子树最后是根节点,重建代码如下:

void build(int u)
{if(u <= n)                   //下标不能越界{build(2 * u + 1);        //先遍历右子树build(2 * u);            //然后是左子树cin>>tree[u];            //tree数组用来存树mp[tree[u]] = u;         //mp用来标记某一节点是否在树中}
}

其次如何标记果子,果子都在最后一层,果子之上每一层都是满的,从上到小每一层满的个数依次是:1,2,4,8……,计算每一层上面所有的节点个数(包含本层)依次是:1,3,7,15……这样我们就可以发现规律,我们按照满二叉树的节点个数进行计算,如果从第一层到该层的节点个数小于满二叉树所应有的节点个数,那么就可以判断该层就是最后一层,注意特判一下原本就是满二叉树的情况,代码如下:

int bits[] = {0 , 1 , 3 , 7 , 15 , 31 , 63};        //节点个数不超过60,所以开到63即可int x = 0;
while(bits[x] < n)                                  //统计最后一层满足满二叉树节点个数的层数x++;                                            //x层是满足条件的下一层for(int i = bits[x - 1] + 1 ; i <= n ; i++)          //从x-1层到树的最后一个节点,都是果子,用bool数组进行标记isFruit[i] = true;

然后再说如何判断给定节点编号是否在树中,其实在重建树的时候就提到了,直接在建树的时候就开数组标记即可。

再说如何判断被砍掉,还记得我们重建的时候用tree数组去存树吗,由于每个节点编号都是正整数,那么我们在砍树的过程中,直接把要砍掉的节点tree的值置为零即可。

然后是重头戏,如何模拟砍树?首先明确砍树的过程,从一个节点出发,将自己和自己的子树全部砍掉,这里我们可以递归遍历去写,根据上面说的,砍树时,每走到一个点,就把这个节点对于的tree的值置为空。并且由于我们之前标记了果子,所以每走到一个果子节点,结果就加一,再递归地计算左子树的果子个数和右子树的果子个数即可,代码如下:

int pick(int u)
{int cnt = 0;                        //初始化果子个数if(u <= n && tree[u])               //下标没有越界并且该节点没被砍掉{if(isFruit[u])                  //如果当前节点是果子,则个数加一cnt++;    tree[u] = 0;                    //将该节点状态置为砍掉了cnt += pick(2 * u);             //递归计算左子树果子个数cnt += pick(2 * u + 1);            //递归计算左子树果子个数}return cnt;                         //返回本次砍树得到的果子个数
}

最后前序遍历输出就比较简单了,从根节点出发,先遍历左子树,再遍历右子树,最后是根节点,注意,这里要跳过被砍掉了的节点,代码如下:

void preTravel(int u)
{if(u <= n && tree[u])                                    //下标不能越界并且没有被砍掉{ans.push_back(tree[u]);                              //将当前节点加入结果数组preTravel(2 * u);                                    //递归遍历左子树preTravel(2 * u + 1);                                //递归遍历右子树}
}preTravel(1);                                               //从根节点出发开始前序遍历
for(int i = 0 ; i < ans.size() ; i++)cout<<ans[i]<<(i == ans.size() - 1 ? "\n" : " ");       //避免行末空格输出

代码:

//L2-1 红豆生南国 (25 分)
#include<vector>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int N = 1010;int n , k , x;
int tree[N] , mp[N];
bool isFruit[N];
vector<int> ans;
int bits[] = {0 , 1 , 3 , 7 , 15 , 31 , 63};void build(int u)            //重建二叉树
{if(u <= n){build(2 * u + 1);build(2 * u);cin>>tree[u];mp[tree[u]] = u;}
}int pick(int u)               //摘果子
{int cnt = 0;if(u <= n && tree[u]){if(isFruit[u])cnt++;tree[u] = 0;cnt += pick(2 * u);cnt += pick(2 * u + 1);  }return cnt;
}void preTravel(int u)        //前序遍历
{if(u <= n && tree[u]){ans.push_back(tree[u]);preTravel(2 * u);preTravel(2 * u + 1);}
}int main()
{cin>>n;build(1);int x = 0;while(bits[x] < n)x++;for(int i = bits[x - 1] + 1 ; i <= n ; i++)    isFruit[i] = true;cin>>k;while(k--){cin>>x;if(!mp[x])                               //节点不在树中cout<<"Kan Qing Chu Le?"<<endl;else if(!tree[mp[x]])                    //该节点被砍掉了cout<<"Zao Jiu Cai Diao Le!"<<endl;else                                     //模拟砍树cout<<pick(mp[x])<<endl;}if(!tree[1])                                 //树根都被砍掉了cout<<"Kong Le!"<<endl;else{preTravel(1);for(int i = 0 ; i < ans.size() ; i++)cout<<ans[i]<<(i == ans.size() - 1 ? "\n" : " ");}return 0;
} 

软件学院天梯赛参赛队员第一次训练 L2-1 红豆生南国 (25 分)(完全二叉树,树的遍历,重建二叉树,DFS)相关推荐

  1. 【PTA天梯赛CCCC -2017决赛L2-4】部落 (25 分)(图染色)

    题干: 在一个社区里,每个人都有自己的小圈子,还可能同时属于很多不同的朋友圈.我们认为朋友的朋友都算在一个部落里,于是要请你统计一下,在一个给定社区中,到底有多少个互不相交的部落?并且检查任意两个人是 ...

  2. 团体程序设计天梯赛-练习集 L2-028 秀恩爱分得快 (25 分) (详细解法)

    题目链接:L2-028 秀恩爱分得快 题目: 古人云:秀恩爱,分得快. 互联网上每天都有大量人发布大量照片,我们通过分析这些照片,可以分析人与人之间的亲密度.如果一张照片上出现了 K 个人,这些人两两 ...

  3. 团体程序设计天梯赛-练习集-L2-040 哲哲打游戏 (25 分)

    哲哲是一位硬核游戏玩家.最近一款名叫<达诺达诺>的新游戏刚刚上市,哲哲自然要快速攻略游戏,守护硬核游戏玩家的一切! 为简化模型,我们不妨假设游戏有 N 个剧情点,通过游戏里不同的操作或选择 ...

  4. PTA_2021年团体程序设计天梯赛_总决赛_L2-4 哲哲打游戏 (25 分)_搞心态_简单模拟

    // 输出格式: 对于每个 1(即存档)操作,在一行中输出存档的剧情点编号.最后一行输出哲哲最后到达的剧情点编号.输入样例: 10 11 3 2 3 4 1 6 3 4 7 5 1 3 1 9 2 3 ...

  5. 重返天梯-L2-036 网红点打卡攻略 (25 分)

    重返天梯-L2-036 网红点打卡攻略 (25 分) C++ 有点小坑,所以记录一下 #include <bits/stdc++.h> using namespace std; const ...

  6. CCCC 天梯赛 PTA ZOJ 题目 L1 L2 L3

    PTA 天梯赛题目整理 L2 难点 **L2-001 紧急救援** **L2-002 链表去重** **L2-003 月饼** **L2-004 这是二叉搜索树吗** **L2-005 集合相似度** ...

  7. 2019天梯赛第四次训练赛

    7-1 表达式转换 (25 分) 算术表达式有前缀表示法.中缀表示法和后缀表示法等形式.日常使用的算术表达式是采用中缀表示法,即二元运算符位于两个运算数中间.请设计程序将中缀表达式转换为后缀表达式. ...

  8. 【2022团体程序设计天梯赛】GPLT2022,L1~L2部分(PTA,L1-081~L1-088,L2-041~L2-044)题解代码复盘

    文章目录 概要 **L1-081 今天我要赢** (5分) **L1-082 种钻石**(5分) **L1-083 谁能进图书馆**(10分) **L1-084 拯救外星人**(10分) **L1-0 ...

  9. PTA天梯赛L1刷题总结(三)15分题型(超详细题解)

    多么感人!时隔一年多,我终于来更新15分题型的博文了.突然发现L1的题目量扩充了!一共有哦20道题.哎~都写一遍题解好了.在这里推荐下胡凡的算法笔记!在基础算法和数据结构上给了我很多细致的讲解启发.过 ...

最新文章

  1. mysql 开发基础系列18 存储过程和函数(下)
  2. 鸿蒙系统的逻辑,鸿蒙系统逻辑近似苹果iOS13?这华为在搞什么?
  3. centos7删除符号链接_技术|在 Linux 中怎样移除(删除)符号链接
  4. background-image使用svg如何改变颜色
  5. 传世的关系模型,巧夺天工的分布式数据库设计
  6. python 深度 视差 计算_2,Learn about Parallax(视差贴图)
  7. 项目中的软件需求说明书的访谈部分
  8. DFS--01背包问题
  9. 关于annotation object的旋转
  10. error: src refspec main does not match any
  11. 南京大学计算机学院冯新宇导师,中国科学技术大学计算机科学与技术专业硕士研究生导师冯新宇...
  12. 使用PLC编程(ST语言)开发的DES加密解密功能块
  13. “黑桃A” 11月19日团队实训总结
  14. 花 3 分钟时间了解技术人应具备的思维能力 - 抽象
  15. 【安卓小程序】仿微信页面
  16. Android App links 链接打开app功能
  17. python-英文字母的大小写转换
  18. 计算机取消uefi启动项,如何使用老毛桃winpe删除或添加UEFI BIOS启动项?
  19. Scrapy第十五篇:后起之秀-Playwright
  20. 学习笔记之English

热门文章

  1. Linux 入门级 常用命令 日常记录
  2. 太厉害了,目前 Redis 可视化工具最全的横向评测
  3. 智能优化算法-阿里巴巴和四十大盗算法Ali baba and the Forty Thieves algorithm(附Matlab代码)
  4. PB9.0连接MSSQL2005和MSSQL2008数据库出错!unable to load the requested database interface.
  5. 最新kali之clang++
  6. 深入理解mongoose
  7. 微信小程序短信验证登录
  8. 后序遍历的非递归算法python_后的解释|后的意思|汉典“后”字的基本解释
  9. python聊天室_python实现简单多人聊天室
  10. 机器学习之学习曲线绘制Python-skleran