UVA679 小球下落 Dropping Balls
题目翻译:
许多的小球一个一个的从一棵满二叉树上掉下来组成一个新满二叉树,每一时间,一个正在下降的球第一个访问的是非叶子节点。然后继续下降时,或者走右子树,或者走左子树,直到访问到叶子节点。
决定球运动方向的是每个节点的布尔值。最初,所有的节点都是 FALSE,当访问到一个节点时,如果这个节点是 FALSE,则这个球把它变成 TRUE,然后从左子树走,继续它的旅程。如果节点是TRUE,则球也会改变它为 FALSE,而接下来从右子树走。满二叉树的标记方法如下图。
因为所有的节点最初为 FALSE,所以第一个球将会访问节点 1,节点 2 和节点 4,转变节点的布尔值后在在节点 8 停止。第二个球将会访问节点 1、3、6,在节点 12 停止。明显地,第三个球在它停止之前,会访问节点 1、2、5,在节点 10 停止。
现在你的任务是,给定新满二叉树的深度 d 和下落的小球的编号 i ,可以假定I不超过给定的新满二叉树的叶子数,写一个程序求小球停止时的叶子序号p。
思路:
有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个小球;
再看往右落的球:
右落的球编号 | 2 | 4 | 6 | 8 |
---|---|---|---|---|
I / 2 | 1 | 2 | 3 | 4 |
代码描述
举个栗子:
输入4 2(高度为4,有2个球要下落)
接模拟最后一个球(编号为2,蓝色表示)的下落过程,树总共有D层,很明显,小球只需要下落D-1层就一定到达叶子节点。
设置k表示该球处于树上的结点编号,初始为1(在根结点处)
I表示的是对于所处的树结点来说,第几个经过该结点的球。(初始为I,因为相对于根结点来说,最后一个下落的球是第I个经过根结点的球)
I = 2 为偶数,说明它是根结点处第1(I / 2 = 1 )个往右子树下落的球,让它下落一层去往右子结点。所处结点更新k = 2 ∗ k + 1 = 3 ,,I/2=1
I=1为奇数,说明它是树结点3这里第1(( I + 1 ) / 2 = 1)个往左子树下落的球,让它下落一层去往左子结点。所处结点更新k = 2 ∗ k = 6 ,(I+1)/2=1
I=1为奇数,说明它是树结点6这里第1(( I + 1 ) / 2 = 1)个往左子树下落的球,让它下落一层去往左子结点。所处结点更新k = 2 ∗ k = 12 ,(I+1)/2=1
得知,该球下落了D − 1 = 4 − 1 = 3 D-1 = 4-1 = 3D−1=4−1=3层后所处叶结点的编号为12。
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <string>
#include <cmath>
#include <vector>
#include <map>
#include <set>
#include <queue>
using namespace std;
int main()
{
int l;
while(scanf("%d",&l)&&l != -1){
while(l--){
int D,I;
scanf("%d%d",&D,&I);
int k = 1;
for(int i = 0; i < D - 1; i++){
if(I % 2){
k = 2 * k;
//这个球是走向左子树的第几个球
I = (I + 1)>>1;
}
else{
k = 2 * k + 1;
//这个球是走向右子树的第几个球
I = I>>1;
}
}
printf("%d\n",k);
}
}
return 0;
}