cen114514

导航

【洛谷P1185 绘制二叉树】题目分析

【洛谷题目链接:P1185
一道薛微显简单的绿题~
↓ 开盘 ↓

题目


题目描述

二叉树是一种基本的数据结构,它要么为空,要么由根结点,左子树和右子树组成,同时左子树和右子树也分别是二叉树。

当一颗二叉树高度为 m-1 时,共有 m 层。若一棵二叉树除第 m 层外,其他各层的结点数都达到最大,且叶子结点都在第 m 层时,则其为一棵满二叉树。

现在,需要你用程序来绘制一棵二叉树,它由一棵满二叉树去掉若干结点而成。对于一棵满二叉树,我们需要按照以下要求绘制:

  1. 结点用小写字母 o 表示,对于一个父亲结点,用 / 连接左子树,用 \ 连接右子树。

  2. 定义 [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] 为 \

  3. 对于第 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");
	}
}

完结撒花(๑>؂<๑)

posted on 2025-08-26 20:14  CHNcen  阅读(17)  评论(0)    收藏  举报