【C/C++】小球下落(Dropping Balls)-算法竞赛入门经典6-6

有一颗二叉树,最大深度为D,所以有2^D-1个节点。
【本题中学到的有】

  1. 2^D在C中的表示,用了一个很巧妙地方法 1<<D
    <<是左移运算符,x<<y为x*2^y
  2. while(scanf("%d%d",&a,&b)==2)
    二元输入老套路了
  3. 路径存储,我这里用了一个vector试了一下,但是用到c++的stl显然会比较慢,所以我注释掉的printf部分从输出来说还可以。
    如果不clear的话,vector是可以一直存储路径下去的。
    另外,我本来想做一个多维数组来保存每一条路径。但是由于数组最开始需要声明整体内存大小,不能用后来读取的变量,所以放弃了。(s1是本来想使用但是失败了的)
    【源码】
#include<stdlib.h>
#include<stdio.h>
#include<string.h>
#include<vector>
#include<iostream>
using namespace std;

//树高D最大为20
const int maxn = 20;
//最大节点个数为1<<maxn
//数组s用于记录每个节点当前的开关状态,0表示关,向左;1表示开,向右
int s[1<<maxn]; //用于存储每个点的开关
vector<int> ss; //用于存储一条路径

int main(){
    int D,I;
    while(scanf("%d%d",&D, &I)==2){
        //输入一组数据,D深的树,I个待检数据
        int n = (1<<D)-1;
        int k = 0;
        //0表示关,向左;1表示开,向右。每一层下一个的左节点为自己*2,右节点为自己*2+1 
        //-> 下一层的节点为【自己*2+自己的开关值】
        memset(s,0,sizeof(s)); //清空数组,全部置零,全部为关;
        for (int i = 0; i < I; i++){
            //开始考察每个小球
            //memset(s1,0,sizeof(s1));
            k = 1;
            ss.clear();
            for (int j = 0; j < D; j++){
                k = 2*k + s[k-1];
                s[k-1] = !s[k-1];
                ss.push_back(k);
                //printf("%d ",k);
            }
            //vector输出必须通过迭代器
            for (vector<int>::iterator i=ss.begin(); i!=ss.end(); i++){
                cout << *i << " ";
            }
            cout << endl;
        }
    }
    system("pause");
}

【代码示例】
五层树的三个球情况:
在这里插入图片描述


书中给出了第二个方法:根据考察的第I个小球的数字编号来看落在哪里。
这个方法完全是数学想法,根据这个编号的数字来看。
对于根节点1号,由于每个球都要经过,所以根节点上的第I个经过的,如果I是奇数,那么就是往左走,如果I是偶数,那么就是往右走。
当I是奇数的时候,它是往左走的第(I+1)/2个小球。
当I是偶数的时候,它是往右走的第I/2个小球。
也就是对于下一个节点:
【左】如果(I+1)/2是奇数,那再往左走;
【右】如果I/2是偶数,那再往右走。

#include<stdlib.h>
#include<stdio.h>
#include<string.h>
#include<vector>
#include<iostream>
using namespace std;

//树高D最大为20
//const int maxn = 20;
//最大节点个数为1<<maxn
//数组s用于记录每个节点当前的开关状态,0表示关,向左;1表示开,向右
//int s[1<<maxn]; //用于存储每个点的开关
//vector<int> ss; //用于存储一条路径

int main(){
    int D,I;
    while(scanf("%d%d",&D, &I)==2){
        //输入一组数据,D深的树,I个待检数据
        int n = (1<<D)-1;
        //int k = 0;
        int k = 1;
        for (int i = 0; i < D; i++){
            printf("%d ",k);
            if(I%2) { k = k*2; I = (I+1)/2; }
            else { k = k*2+1; I /= 2;}
        }
        //0表示关,向左;1表示开,向右。每一层下一个的左节点为自己*2,右节点为自己*2+1 
        //-> 下一层的节点为【自己*2+自己的开关值】
        //memset(s,0,sizeof(s)); //清空数组,全部置零,全部为关;
        /*
        for (int i = 0; i < I; i++){
            //开始考察每个小球
            //memset(s1,0,sizeof(s1));
            k = 1;
            ss.clear();
            for (int j = 0; j < D; j++){
                k = 2*k + s[k-1];
                s[k-1] = !s[k-1];
                ss.push_back(k);
                //printf("%d ",k);
            }
            //vector输出必须通过迭代器
            for (vector<int>::iterator i=ss.begin(); i!=ss.end(); i++){
                cout << *i << " ";
            }
            cout << endl;
           
        }
        */
    }
    system("pause");
}

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值