【洛谷P1185 绘制二叉树】题目分析
【洛谷题目链接:P1185】
一道薛微显简单的绿题~
↓ 开盘 ↓
题目
题目描述
二叉树是一种基本的数据结构,它要么为空,要么由根结点,左子树和右子树组成,同时左子树和右子树也分别是二叉树。
当一颗二叉树高度为 m-1 时,共有 m 层。若一棵二叉树除第 m 层外,其他各层的结点数都达到最大,且叶子结点都在第 m 层时,则其为一棵满二叉树。
现在,需要你用程序来绘制一棵二叉树,它由一棵满二叉树去掉若干结点而成。对于一棵满二叉树,我们需要按照以下要求绘制:
-
结点用小写字母
o表示,对于一个父亲结点,用/连接左子树,用\连接右子树。 -
定义 [i,j] 为位于第 i 行第 j 列的某个字符。若 [i,j] 为
/,那么 [i-1,j+1] 与 [i+1,j-1] 要么为o,要么为/。若 [i,j] 为\,那么 [i-1,j-1] 与 [i+1,j+1] 要么为o,要么为\。同样,若 [i,j] 为第 1~m-1 层的某个结点o,那么 [i+1,j-1] 为/,[i+1,j+1] 为\。 -
对于第 m 层结点也就是叶子结点点,若两个属于同一个父亲,那么它们之间由 3 个空格隔开;若两个结点相邻但不属于同一个父亲,那么它们之间由 1 个空格隔开。第 m 层左数第 1 个结点之前没有空格。
最后需要在一棵绘制好的满二叉树上删除 n 个结点(包括这个结点的左右子树,以及与父亲的连接),原有的字符用空格替换(空格为 ASCII 32,若输出 ASCII 0 会被算作错误答案)。
输入格式
第 1 行包含 2 个正整数 m 和 n,为需要绘制的二叉树层数和需要删除的结点数。
接下来 n 行,每行两个正整数,表示删除第 i 层的第 j 个结点。
输出格式
按照题目要求绘制的二叉树。
输入输出样例 #1
输入 #1
2 0
输出 #1
o
/ \
o o
输入输出样例 #2
输入 #2
4 0
输出 #2
o
/ \
/ \
/ \
/ \
/ \
o o
/ \ / \
/ \ / \
o o o o
/ \ / \ / \ / \
o o o o o o o o
输入输出样例 #3
输入 #3
4 3
3 2
4 1
3 4
输出 #3
o
/ \
/ \
/ \
/ \
/ \
o o
/ /
/ /
o o
\ / \
o o o
说明/提示
30% 的数据满足:n = 0;
50% 的数据满足:2 ≤ m ≤ 5;
100% 的数据满足:2 ≤ m ≤ 10, 0 ≤ n ≤ 10, 1 < i ≤ m, j ≤ 2^(j-1)。
思路:初步构图
这是一道模拟题,所以我们用BFS做 (bushi
首先我们先来解决构图的问题 (来找找规律吧...)
先来盘盘总列数(长)的关系
- n=2: 5
- n=3: 11(2个n=2的长+1)
- n=4: 23(4个n=2的长+3)
...
不难总结出:
n=i时,长为 3x2^(i-1)-1
于是其根节点的横轴坐标便为 3x2^(i-2)
有了起点 不就可以广搜了嘛~
接下来便是每个单元树(人话:除去重复的)
由于本蒻蒟不喜欢字符存储,我们就规定些数字来代表符号吧,看着也方便些
- 0 :空格
- 1 :左子树连接 /
- -1:右子数连接 \
- 2 :节点 o
*n=2的单元树组成:
0 0 2 0 0
0 1 0 -1 0
2 0 0 0 2
整体是一个3x5的矩形(题目也没有说要除去多余空格对吧hhh)
*n=3的单元树组成:
0 0 0 2 0 0 0
0 0 1 0 -1 0 0
0 1 0 0 0 -1 0
2 0 0 0 0 0 2
4x7的矩形
后面的自己动手数一下吧,也不能太依赖别人是不是(真不是我嫌手打麻烦不想写...
简单,不就是往左下和右下扩展嘛
来想想边数(或者说宽/层数)的关系吧~
- n=2:长 5 边数 1
- n=3:长 7 边数 2
- n=4:长 13 边数 5
一开始看到5条边的时候会不会有点奇怪呢?可能会有人(比如我!)认为这里是4才对吧hh
不过既然我把长写在这里,就肯定是有关系滴
看下推导: ↓
(5-3)/2=1 & (7-3)/2=2 & (13-3)/2=5
为什么是这样的呢?其实将总长减去三个结点,就是连接子树的斜线字符的水平长,且每一层都有两个连接(一个1,一个-1),所以除以二理应就是单边的边数啦~
0 0 0 X 0 0 0
0 0 / 0 \ 0 0
0 / 0 0 0 \ 0
X 0 0 0 0 0 X
关系式:n=i : [3x2^(i-1)-4]/2
那么,初步构图的问题就解决啦~
代码如下:
点击查看代码
const int maxn = 1e4;
const int m2t[11]={1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024};//减去累乘或快速幂的操作,降低时间复杂度
int m, n, mapn[maxn][maxn];
struct node {
int x, y, mi;
};//x, y为当前结点坐标, mi为当前层数(不过是倒序的)
queue<node> q;
int main() {
scanf("%d%d", &m, &n);
node start = {1, m2t[m-2]*3, m, 1};//已解释过
mapn[start.x][start.y] = 2;
q.push(start);
while(!q.empty()) {
node h = q.front();
q.pop();
int l[2] = {h.x+1, h.y-1}, r[2] = {h.x+1, h.y+1};
for(int i=1; i<=max((3*m2t[h.mi-2]-2)/2, 1); i++) {//也解释过了^~^
mapn[l[0]][l[1]] = 1; mapn[r[0]][r[1]] = -1;
l[0]++; l[1]--; r[0]++; r[1]++;//向左下或右下延伸
}
mapn[l[0]][l[1]] = 2;
mapn[r[0]][r[1]] = 2;//标记根结点
if(h.mi > 2) {
node li = {l[0], l[1], h.mi-1, h.ki*2-1};
node ri = {r[0], r[1], h.mi-1, h.ki*2};
q.push(li);
q.push(ri);//将根节点加入队列中
}
}
}
删除结点
但是如果只是找规律画图,这题就不会是道绿题了(&_&
难点就在于删除怎么做
这时,我们使用BFS的好处就体现出来了
我们只需要在父亲结点处判断是否有儿子结点被删除
为方便,我们可以在node结构体中引入一个新变量ki,用于记录该结点在此层的序号
满二叉树嘛,每个父亲结点都会有两个儿子结点
- 序号变化:k => 2k-1 & 2k
每次延伸的时候遍历一遍需要被删除的结点,如果子结点中有就不进行之后的操作了
完整代码如下:
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e4;
const int m2t[11]={1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024};
int m, n, delet[maxn][2], mapn[maxn][maxn];
struct node {
int x, y, mi, ki;//新增ki
};
queue<node> q;
int main() {
scanf("%d%d", &m, &n);
for(int i=1; i<=n; i++)
scanf("%d%d", &delet[i][0], &delet[i][1]);
node start = {1, m2t[m-2]*3, m, 1};
mapn[start.x][start.y] = 2;
q.push(start);
while(!q.empty()) {
node h = q.front();
q.pop();
bool l_check = 0, r_check = 0;//定义两个布尔类型变量,若为1则删除
for(int i=1; i<=n; i++)
if(m - h.mi + 2 == delet[i][0]) {//判断层数是否相等(这里是逆序的)
if(h.ki*2-1 == delet[i][1]) l_check = 1;
if(h.ki*2 == delet[i][1]) r_check = 1;//判断列数是否相等
}
if(l_check == 0) {//分开处理
int l[2] = {h.x+1, h.y-1};
for(int i=1; i<=max((3*m2t[h.mi-2]-2)/2, 1); i++) {
mapn[l[0]][l[1]] = 1;
l[0]++; l[1]--;
}
mapn[l[0]][l[1]] = 2;
if(h.mi > 2) {
node li = {l[0], l[1], h.mi-1, h.ki*2-1};
q.push(li);
}
}
if(r_check == 0) {
int r[2] = {h.x+1, h.y+1};
for(int i=1; i<=max((3*m2t[h.mi-2]-2)/2, 1); i++) {
mapn[r[0]][r[1]] = -1;
r[0]++; r[1]++;
}
mapn[r[0]][r[1]] = 2;
if(h.mi > 2) {
node ri = {r[0], r[1], h.mi-1, h.ki*2};
q.push(ri);
}
}
}
for(int i=1; i<=3*m2t[m-2]; i++) {//将int类型二位数组转化为字符输出
for(int j=1; j<=6*m2t[m-2]-1; j++)
if(mapn[i][j] == 0) printf(" ");
else if(mapn[i][j] == 1) printf("/");
else if(mapn[i][j] == -1) printf("\\");
else if(mapn[i][j] == 2) printf("o");
printf("\n");
}
}
浙公网安备 33010602011771号