小球下落 Dropping Balls

小球下落 Dropping Balls

题面翻译

更详尽翻译请参考紫书。

许多的小球一个一个的从一棵满二叉树上掉下来组成一个新满二叉树,每一时间,一个正在下降的球第一个访问的是非叶子节点。然后继续下降时,或者走右子树,或者走左子树,直到访问到叶子节点。
决定球运动方向的是每个节点的布尔值。最初,所有的节点都是 FALSE,当访问到一个节点时,如果这个节点是 FALSE,则这个球把它变成 TRUE,然后从左子树走,继续它的旅程。如果节点是TRUE,则球也会改变它为 FALSE,而接下来从右子树走。满二叉树的标记方法如下图。
因为所有的节点最初为 FALSE,所以第一个球将会访问节点 1,节点 2 和节点 4,转变节点的布尔值后在在节点 8 停止。第二个球将会访问节点 1、3、6,在节点 12 停止。明显地,第三个球在它停止之前,会访问节点 1、2、5,在节点 10 停止。
现在你的任务是,给定新满二叉树的深度 d 和下落的小球的编号 i ,可以假定I不超过给定的新满二叉树的叶子数,写一个程序求小球停止时的叶子序号p。

题目描述

PDF

输入格式

输出格式

样例

样例输入 #1

5
4 2
3 4
10 1
2 2
8 128

样例输出 #1

12
7
512
3
255

思路分析

一开始可以以一种稍显“粗暴”的方式去分析问题,小球会一层层掉落,已知高度有限且易知一个简单的事实,单看一条走到叶结点的路径,其经过的所有非叶结点都对应一个深度,把最深的叶结点提前算好,那么第i个小球从根结点开始掉落经过的所有非叶结点在对应深度上的编号就是i,并用数组记录每个结点经过对应深度的树的编号即可,判断向左还是向右用一个标记数组记录即可。(紫书上有更精妙的解法,以下作为一种思路的开拓)

代码实现

#include <iostream>
#include <cstring>
using namespace std;
const int I = 524288;
const int MAXDEEP = 20;
int num[I + 1][MAXDEEP + 1]; // 在给定的n个小球和深度depth下,用于存放最后一个小球落到的位置
int tree[1024 * 1024]; // 一棵最深的满二叉树对应的结点树量是:2^20
int main(){
    memset(tree, 0, sizeof(tree));
    for(int i = 1; i <= I; i++){
        int idx = 1;
        for(int j = 1; j <= MAXDEEP; j++){
            num[i][j] = idx; // 记录小球的编号i
            if(tree[idx] == 0){ // 0向左走
                tree[idx] = !tree[idx]; // 改变开关状态
                idx = 2 * idx;
            }else{ // 1向右走
                tree[idx] = !tree[idx];
                idx = 2 * idx + 1;
            }
        }
    }
    int caseNumber;
    int depth, n;
    cin >> caseNumber;
    while(caseNumber--){ // 利用已计算好的结果直接输出
        cin >> depth >> n;
        cout << num[n][depth] << endl;
    }
    return 0;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值