UVa679小球下落(二叉树的编号)

树是n个元素的有限集合,不再是序列,其中\(n>=0\)。树可以看成无共享纯广义表。

二叉树的3个要素,根结点,左子树和右子树。二叉树不是树,树的两个要素是根结点和子树森林。

无论线性结构还是树形结构,第一个元素都没有前驱。线性结构的最后一个元素都没有后继,树形结构有多个叶子结点,都没有后继。对于中间的一般元素,树形结构有一个前驱,多个后继,线性结构有一个前驱(无共享),一个后继。

完全二叉树是从上到下从左到右依次排过来,整棵树有唯一的深度取值。丰满二叉树是每一层都排满的完全二叉树,有\(2^i-1\)个结点,每层的结点数目是\(2^{i-1}\)个,其中i是深度。

完全二叉树的从上到下从左到右编号上有规律和结论:编号为1,没双亲,不为1就有双亲;\(2i>n\),没有左孩子,否则就是左孩子;\(2i+1>n\),没有右孩子,否则就是右孩子。

采用顺序存储,完全二叉树不浪费空间,各种运算简单,由上述性质,求双亲求孩子均为常量算法。


模拟

对于这道题目来说,可以模拟小球下落的过程。题目告诉了最大深度为D,所有叶子深度都相同,就告诉了这是一棵丰满二叉树,初始所有结点的开关全部关闭。如果把0当作关闭,1当作打开,那么当小球到达一个结点时,就是先走再改变状态。判断,如果是关闭,往左走,如果是打开就往右走,直到走到叶子结点。

题目给定深度D和小球个数I,由深度D就可以求出最大的小球编号n = \(2^D-1\),接下来就利用上述性质进行模拟。

深度不超过20,编号最大是2的20次方-1,接近1000000的结点个数。

表示2的次方可以不用cmath里的pow(),而是用二进制计算,用1来移位。逻辑运算布尔非改变开关状态。

从这个数据范围以及多组测试数据来看,肯定会超时2 ≤ D ≤ 20, and 1 ≤ I ≤ 524288.

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std;
const int MAXN = 1000010;
int s[MAXN];
int D, I;
 
int main() {
	int T;
	scanf("%d", &T);	
	while (T--) {
		if (scanf("%d%d", &D, &I) != 2) {
			break;
		}
		memset(s, 0, sizeof(s));
		int n = (1<<D)-1;
		int last = 1;
		for (int i = 0; i < I; i++) {
			int cur = 1;
//			printf("小球%d\n", i);
			while (cur <= n) {
//				printf("%d\n", cur);
				last = cur;	
				if (s[cur]) {
					cur = (cur<<1)+1;
				} else {
					cur = cur<<1;
				}
				s[last] = !s[last]; // 逻辑运算,cur改变了,应该改的是原来的开关,看的是改之前的值
			}
		}
		printf("%d\n", last);
			
	}

	return 0;
}
只看一个小球

对于一个结点,必然是上一个往左下一个往右。对于根结点,奇数小球往左走,偶数小球往右走,所以只需看小球编号就知道怎么走。以下所有结点都是如此。

例子,4个小球里面就有2个往左2个往右。也就对应了2个来到左孩子,2个来到右孩子,改变孩子结点的状态。

第1个小球是第1个往左的,第2个小球是第一个往右的,第3个小球是第2个往左的,第4个小球是第2个往右的。

所以把走小球变成走深度就可以了,只看最后一个小球。

这样不仅节省大大时间,而且节省空间。

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std;

int D, I;
 
int main() {
	int T;
	scanf("%d", &T);	
	while (T--) {
		if (scanf("%d%d", &D, &I) != 2) {
			break;
		}
		int n = (1<<D)-1;
		int k = 1;
		for (int i = 2; i <= D; i++) {
			if (I % 2 == 1) {
				k = (k << 1);
				I = (I + 1) >> 1;	
			} else {
				k = (k << 1) + 1;
				I = I >> 1;
			}
		}
		printf("%d\n", k);
			
	}

	return 0;
}
posted @ 2021-02-18 10:45  Mo_hw  阅读(67)  评论(0编辑  收藏  举报