紫书P148例题。
题目大意:小球从一棵所有叶子深度相同的二叉树的顶点开始向下落,树开始所有节点都为0。若小球落到节点为0的则往左落,否则向右落。并且小球会改变它经过的节点,0变1,1变0。给定树的深度D和球的个数I,问第I个小球会最终落到哪个叶子节点。
题意容易理解,紫书上给了一个模拟的做法,但这样会超时。后面的想法我觉得很巧妙。
对于根节点,很容易知道当球的编号为奇数时,球落入左子树,偶数时落在右子树。这样其实对于其它节点看成根节点时也是一样的。比如对于第7个球,为奇数,是第7个到达一号节点的球,也是第(7/2)+1=4个到达2号节点球,再往下也是第2个到达5号节点的球。。。。
所以可以直接模拟最后一个球:
1 #include<iostream> 2 #include<cstring> 3 #include<cstdio> 4 using namespace std; 5 const int maxn=20; 6 int s[1<<20]; 7 8 int main() 9 { 10 int T; 11 cin>>T; 12 int D,I; 13 while(scanf("%d",&D)) 14 { 15 if(D==-1) break; 16 scanf("%d",&I); 17 int k=1; 18 for (int i=0;i<D-1;i++)//从根节点开始只需走D-1次 19 { 20 if(I&1){ //是奇数号球 21 k=k*2; //就往左子树走 22 I=(I+1)/2; //相当于是经过当前结点的第(I+1)/2个球,用来判断下回球往哪走 23 }else{ //是偶数号球 24 k=k*2+1; //就往右子树走 25 I/=2; //相当于是经过当前节点的第(I/2)个球,用来判断下回球往哪走 26 } 27 } 28 cout<<k<<endl;//输出球到的位置 29 } 30 return 0; 31 }
也有人从哈夫曼编码角度理解,结果正好是I-1的二进制逆序,感觉很厉害!
#include<iostream> #include<cstring> #include<cstdio> using namespace std; const int maxn=20; int s[1<<20]; int main() { int T; cin>>T; int D,I; while(scanf("%d",&D)) { if(D==-1) break; scanf("%d",&I); int k=1;//k初始化为1,即最高位为1 I-=1; //求I-1的二进制逆序: while(--D) { if(I&1){ k=(k<<1)+1; }else{ k<<=1; } I>>=1; } cout<<k<<endl; } return 0; }