Dropping Balls(小球下落)
紫书P148,例题6-6
Sample Input
4 2 3 4 10 1 2 2 8 128
Sample Output
12 7 512 3 255
这应该不仅仅是一棵完全二叉树,题目中说保证所有叶子节点的深度都相同,所以这是一颗满二叉树。
这里要弄清满二叉树的一些概念和性质,首先,对于一颗满二叉树来说,他每一层的节点数都达到最大,那么对于一个K层的满二叉树来说,他的节点数有(2^k)-1个
而且研究满二叉树和完全二叉树的一个好处在于他可以实现顺序存储,如图中的可以表示为
值 :1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
位置:1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
其中i节点的左孩子为2*i,右孩子为2*i+1
掌握了这一点,即使给出的树的值不是严格的从上到下,从左至有右的递增序列,也能很轻松的搞定了
#include <iostream> #include <cstdio> using namespace std; int a[(1<<20)+10]; int main() { int D,N; while(~scanf("%d%d",&D,&N)) { int m=(1<<D)-1; for(int i=1;i<=m;i++) {a[i]=0;} int p; for(int i=1;i<=N;i++) { p=1; while(1) { if(a[p]==0) {a[p]=1;p=p*2;} else {a[p]=0;p=p*2+1;} if(p>(1<<(D-1))-1) break; } } cout<<p<<endl; } return 0; }
可是单单这样进行模拟,时间上太过浪费:
可以直接进行分析,如果一个根节点被访问了5次,那么他的左孩子节点必定被访问了3次,右孩子必定被访问了2次,我们是怎么得出这个结论的呢?
显然当处在某个节点上的球不是往左走就是往右,而且最初是往左的,那么左边必被分到((n+1)/2)次,右边分到(n/2)次
推而广之可得对于n这个小球到达每个点时应该向左还是向右
#include <iostream> using namespace std; int main() { int D,N; while(cin>>D>>N) { int p=1; for(int i=1;i<D;i++) { if(N%2) { p=p*2; N=(N+1)/2; } else { p=p*2+1; N=N/2; } } cout<<p<<endl; } return 0; }