679 - Dropping Balls

679 - Dropping Balls_第1张图片
679 - Dropping Balls_第2张图片
679 - Dropping Balls_第3张图片
Problem.png

看到这题一开始的想法就是直接模拟一棵完全二叉树,然后根据每一层的开关值来决定小球向左走还是向右走。
需要注意的是,若完全二叉树根节点编号为1,则编号为n的结点的左孩子的标号为2n,右孩子的标号为2n+1。
然而。。这题直接模拟会超时。。于是百度之后发现了一种直接根据小球的奇偶性来判断它往哪边落的方法,还是挺强的。。(结果做完才发现书上后面也讲了会超时。。手动再见。。)

直接模拟TLE代码如下:

#include 
#include 

const int maxd = 20;
bool flag[1 << maxd];

int main() {
    int l, D, I;
    scanf("%d", &l);
    while ((scanf("%d %d", &D, &I)) == 2) {
        memset(flag, false, sizeof(flag));
        int last = 0;
        for (int i = 0; i < I; i++) {
            int index = 1, depth = 1;
            while (depth != D) {
                if (!flag[index]) {
                    flag[index] = true;
                    index = 2 * index;
                }
                else {
                    flag[index] = false;
                    index = 2 * index + 1;
                }
                depth++;
            }
            if (i == I - 1) last = index;
        }
        printf("%d\n", last);
    }
}

另一种思路时,对于树中每一个结点,只要知道当前小球是第几个到达该结点的小球,就能知道它下一步是往左走还是往右走,因为开关每次都会反转一下。这样的话就根本不用模拟出二叉树并且每一个小球都落一遍了,很省时间。
以根结点为例,当I为奇数,即第奇数个小球放在根结点时,小球一定是落向左子树,而I为偶数时则落向右子树。之后只需要确定该小球对于左子树或者右子树的根结点是第几个小球,就能完成整个迭代过程。
经找规律可知,对于根结点的第I个球,若I为奇数,则它对于根的左子树的根结点是第(I+1)/2个小球,若I为偶数,则它对于根的右子树的根结点是第I/2个小球。接下来只要继续判断(I+1)/2或I/2的奇偶性,就能知道小球在子树上是往左走还是往右走了。

一个自我感觉是,对于完全二叉树,只需要考虑根和左子树、右子树之间的规律,对于剩下的每一层,规律都是一样的,因为二叉树是递归定义的。

AC代码如下:

#include 
#include 

int main() {
    int l, D, I;
    scanf("%d", &l);
    while ((scanf("%d %d", &D, &I)) == 2) {
        int k = 1;
        for (int i = 0; i < D - 1; i++) {
            if (I % 2 == 1) {
                k = 2 * k;
                I = (I + 1) / 2; // 当I是奇数时,它是往左走的第(I+1)/2个小球
            }
            else {
                k = 2 * k + 1;
                I = I / 2;       // 当I是偶数时,它是往右走的第I/2个小球
            }
        }
        printf("%d\n", k);
    }
}

你可能感兴趣的:(679 - Dropping Balls)