A - Dropping Balls

A number of K balls are dropped one by one from the root of a fully binary tree structure FBT. Each

time the ball being dropped first visits a non-terminal node. It then keeps moving down, either follows
the path of the left subtree, or follows the path of the right subtree, until it stops at one of the leaf
nodes of FBT. To determine a ball’s moving direction a flag is set up in every non-terminal node with
two values, either false or true. Initially, all of the flags are false. When visiting a non-terminal node
if the flag’s current value at this node is false, then the ball will first switch this flag’s value, i.e., from
the false to the true, and then follow the left subtree of this node to keep moving down. Otherwise,
it will also switch this flag’s value, i.e., from the true to the false, but will follow the right subtree of
this node to keep moving down. Furthermore, all nodes of FBT are sequentially numbered, starting at
1 with nodes on depth 1, and then those on depth 2, and so on. Nodes on any depth are numbered
from left to right.
For example, Fig. 1 represents a fully binary tree of maximum depth 4 with the node numbers 1,
2, 3, …, 15. Since all of the flags are initially set to be false, the first ball being dropped will switch
flag’s values at node 1, node 2, and node 4 before it finally stops at position 8. The second ball being
dropped will switch flag’s values at node 1, node 3, and node 6, and stop at position 12. Obviously,
the third ball being dropped will switch flag’s values at node 1, node 2, and node 5 before it stops at
position 10.
Fig. 1: An example of FBT with the maximum depth 4 and sequential node numbers.
Now consider a number of test cases where two values will be given for each test. The first value is
D, the maximum depth of FBT, and the second one is I, the I-th ball being dropped. You may assume
the value of I will not exceed the total number of leaf nodes for the given FBT.
Please write a program to determine the stop position P for each test case.
For each test cases the range of two parameters D and I is as below:
2 ≤ D ≤ 20, and 1 ≤ I ≤ 524288.
Input
Contains l + 2 lines.
Line 1 l the number of test cases
Line 2 D1 I1 test case #1, two decimal numbers that are separated by one blank

Line k + 1 Dk Ik test case #k
Line l + 1 Dl Il test case #l
Line l + 2 -1 a constant ‘-1’ representing the end of the input file
Output
Contains l lines.
Line 1 the stop position P for the test case #1

Line k the stop position P for the test case #k

Line l the stop position P for the test case #l
Sample Input
5
4 2
3 4
10 1
2 2
8 128
-1
Sample Output
12
7
512
3
255

分析:

本题,首先题意我是看不懂的。不得不借助一下翻译。
在了解了本题的题意后,我发现可以根据满二叉树的性质来写这道题。其实也没必要去模拟每个小球的下坠过程。我们如果了解了二叉树的性质之后,我们会发现,在这里我们可以不用建树。而是将每个结点保存在一个数组里面(满二叉树的性质)。在这个一维数组里面,下标为0的结点是根结点。下标为2i的结点是下标为i的结点的左结点。下标为2i+1的结点是下标为i的结点的右结点。那么整个小球的下坠过程,就可以用下标的不断移动来表示。然后,整个一维数组里的每一个数组元素,表示一个结点。我们可以定义一个结构体,结构体里面有值,还有往哪边走的标志值。然后,在定义一个结构体的一维数组。并且,注意在每一个球的下落后,标志值要改变。
如下代码:

#include"stdio.h"
#include"string.h"
typedef struct BiTNode{
    long long node;
    long long left;//在此题中完全可以不用定义左右孩子的结点值下标。
    long long right;
    int mark;
}BiTNode;
BiTNode NODE[10000000];
int main(){
   long long i,D,j,k,T;
   while(~scanf("%lld",&T)){
        if(T==-1)
          break;
     while(T--){
   scanf("%lld",&D);
   for(i=1;i<=pow(2,D)-1;i++){
       NODE[i].node=i;
       NODE[i].left=2*i;
       NODE[i].right=2*i+1;
       NODE[i].mark=0;
   }
   scanf("%lld",&i);
   for(j=1;j<=i;j++){
    for(k=1;k<=pow(2,D-1)-1;){
        if(NODE[k].mark==0){
            NODE[k].mark=1;
            k=NODE[k].left;
        }
        else{
            NODE[k].mark=0;
            k=NODE[k].right;
        }
    }
   }
printf("%lld\n",k);
   }
   }
}

不过当我提交的时候,会发现时间超限。证明此思路可行。但无法通过大量数据。然后,经过思考,我们会发现。对于如果有N(N为偶数)个球。那么首先,可以肯定的是,最后的下落点在根的右侧。同样我们继续考虑。最后的下落点在根的右侧。那么到达根的右孩子的球的个数,只能有N/2个。然后我们再考虑N/2的奇偶。如果为奇,则最后的下落点在根的右孩子的左侧,且有(N/2+1)/2个球落在左侧。否则相反。
如此,一直向下考虑。知道N为1,为止。
如此一来此题,变了一个思维题。
不过在此思路中,我们可以用一个数组来保存一个树的叶结点。不需要考虑双亲结点。
代码如下:

#include"stdio.h"
#include"string.h"
long long a[10000000];//当D为20的时候,叶结点巨多。
int main()
{
    long long D,I,N;

    long long i,j,start,end,mid,length;
    while(~scanf("%lld",&N))
    {  if(N==-1)
        break;
        while(N--)
        {
            scanf("%lld%lld",&D,&I);

            end=start=1;
            for(i=2;i<=D;i++)//利用此循环,可以算出叶结点最左边的值和最右边的值
                {start=2*start;
                 end=2*end+1;
                }
           /* printf("%lld %lld\n",start,end);*/
          length=end-start+1;//叶结点的个数, 也用作数组下标
           for(i=1;i<=length;i++)//将叶结点的值保存
                a[i]=start++;
         /*// for(i=1;i<=length;i++)
         //     printf("%lld ",a[i]);*/
         start=1;//下标
         end=length;
         while(I>1)//这里是核心代码
         {
             if(I%2==0)//如果有偶数个球的处理方式。
             {
                 mid=length/2+start-1;/*这里之所以/2之后还要把最左边的下标值加上,已保证,下标值能正确指向中间的那位数。*/
                 start=mid+1;
                 length=end-start+1;
                 I=I/2;
             }
             else//奇数个球的处理方式
             {
                 mid=length/2+start-1;
                 end=mid;
                 length=end-start+1;
                 I=(I+1)/2;
             }
         }
         printf("%lld\n",a[start]);

        }
    }
}

猜你喜欢

转载自blog.csdn.net/qq_43506138/article/details/84961910