题目
来源
《算法竞赛入门经典》6.3节树和二叉树例题6-6(P236)
内容
有一棵二叉树,最大深度为D,且所有叶子的深度都相同。所有结点从上到下从左到右
编号为1, 2, 3,…, 2D-1。在结点1处放一个小球,它会往下落。每个内结点上都有一个开关,
初始全部关闭,当每次有小球落到一个开关上时,状态都会改变。当小球到达一个内结点
时,如果该结点上的开关关闭,则往左走,否则往右走,直到走到叶子结点,如图6-2所
示。
树是一颗满二叉树。
直接模拟最后一个球的过程
为啥是这样的啊?
有I个球往下落。
比如I = 9,那么就是有9个球往下落,假设他们的编号为1~9,刚开始开关都闭合,只看第一个根结点处的开关:
开始开关闭合,那球1会往左子树落;然后开关打开,球2往右子树落;开关闭合,球3往左子树落…
所以奇数号的球往左子树落,偶数号的球往右子树落。那只看往左子树落的球:
左落的球编号 | 1 | 3 | 5 | 7 | 9 |
---|---|---|---|---|---|
(I+1) / 2 | 1 | 2 | 3 | 4 | 5 |
所以有当I是奇数时,它是往左走的第 ( I + 1 ) / 2 (I+1)/2 (I+1)/2个小球;
再看往右落的球:
右落的球编号 | 2 | 4 | 6 | 8 |
---|---|---|---|---|
I / 2 | 1 | 2 | 3 | 4 |
所以有当I是偶数时,它是往右走的第 I / 2 I/2 I/2个小球;
代码描述
举个栗子:
输入4 2(高度为2,有两个球要下落)
直接模拟最后一个球(编号为2,蓝色表示)的下落过程,树总共有D层,很明显,小球只需要下落D-1层就一定到达叶子节点。
设置k表示该球处于树上的结点编号,初始为1(在根结点处)
I表示的是对于所处的树结点来说,第几个经过该结点的球。(初始为I,因为相对于根结点来说,最后一个下落的球是第I个经过根结点的球)
-
I = 2 I = 2 I=2为偶数,说明它是根结点处第1( I / 2 = 1 I/2 = 1 I/2=1)个往右子树下落的球,让它下落一层去往右子结点。所处结点更新 k = 2 ∗ k + 1 = 3 , I / 2 = 1 k = 2*k+1 = 3,I/2 = 1 k=2∗k+1=3,I/2=1
-
I = 1 I = 1 I=1为奇数,说明它是树结点3这里第1( ( I + 1 ) / 2 = 1 (I+1)/2 = 1 (I+1)/2=1)个往左子树下落的球,让它下落一层去往左子结点。所处结点更新 k = 2 ∗ k = 6 , ( I + 1 ) / 2 = 1 k = 2*k = 6,(I+1)/2 = 1 k=2∗k=6,(I+1)/2=1
-
I = 1 I = 1 I=1为奇数,说明它是树结点6这里第1( ( I + 1 ) / 2 = 1 (I+1)/2 = 1 (I+1)/2=1)个往左子树下落的球,让它下落一层去往左子结点。所处结点更新 k = 2 ∗ k = 12 , ( I + 1 ) / 2 = 1 k = 2*k = 12,(I+1)/2 = 1 k=2∗k=12,(I+1)/2=1
得知,该球下落了 D − 1 = 4 − 1 = 3 D-1 = 4-1 = 3 D−1=4−1=3层后所处叶结点的编号为12。
完整程序
#include<iostream>
#include<cstring>
using namespace std;
const int maxd = 20;
bool s[1<<maxd];
int main(){
//树的高度和将要下落的小球的个数
int d, I;
while(scanf("%d%d", &d, &I) == 2){
/*1. 模拟所有小球的走向
//n为该树结点的最大编号
int k, n = (1<<d)-1;
//初始情况所有开关都为闭合状态
memset(s, 0, sizeof(s));
for(int i=0; i<I; i++){
//每个小球都从1号结点开始下落
k = 1;
while(true){
s[k] = !s[k];//更新开关状态,必须先更新,不然下面k值变化了
//因为开关先转状态了,所以规则反了:开启左落,闭合右落
k = s[k] ? 2*k : 2*k+1;
//落出界了
if(k > n) break;
}
}
*/
//2. 直接模拟最后一个小球的走向
int k = 1;
for(int i=0; i<d-1; i++){
//每个小球都是严格下落d-1层
if(I%2){
//I是奇数,说明它是向左下落的第(I+1)/2个小球
k = 2*k;//往左落一层
I = (I+1)/2;
}
else{
//I是偶数,说明它是向右下落的第I/2个小球
k = 2*k+1;//往右落一层
I = I/2;
}
}
//1. printf("%d\n", k/2);
printf("%d\n", k);
}
return 0;
}