有一颗二叉树,最大深度为D,所有叶子的深度都相同。所有结点从上到下从左到右的编号分别依次是1,2,3,4,~,(2的D次方-1)。在节点1放下一个小球,它会往下落。每个内结点都有一个状态(开或关),初始时,每个内结点都处于关闭状态,当小球经过一个内结点时,开关状态会改变。当为开状态时,小球向左落下;当为关状态时,小球向下落下,直到走到叶子结点。
输出树的深度D,和小球数量I
输出第I个小球落到的结点编号
输入样例:
4 2
3 4
2 2
16 12345
输出样例:
12
7
3
36358
模拟全过程:
#include<stdio.h>
#include<string.h>
const int maxn=20;
int s[1<<maxn]; //最大节点个数为2^maxn-1
int main()
{
int D,I;
while(scanf("%d%d",&D,&I)==2)
{
memset(s,0,sizeof(s)); //开关
int k,n=(1<<D)-1; //n是最大节点编号
for(int i=0; i<I; i++) //连续让I个球下落
{
k=1;
for(;;)
{
s[k]=!s[k];
k=s[k]?k*2:k*2+1; //根据开关状态选择下落方向
if(k>n) //已经落“出界”了
break;
}
}
printf("%d\n",k/2); //出界之前的叶子编号
}
return 0;
}
找规律:
当I是奇数时,它是往左走的第(I+1)/2个小球;当I是偶数时,她是往右走的第I/2个小球。这样,可以直接模拟最后一个小球的路线:
如果I是奇数,它一定会落在左子树,否则,一定是在右子树。以I为3为例,可以将它看作是从节点2开始降落的第二个球,依此类推。
#include<stdio.h>
int main()
{
int D,I;
while(scanf("%d%d",&D,&I)==2)
{
int k=1;
for(int i=0; i<D-1; i++)
if(I%2)
{
k=2*k;
I=(I+1)/2;
}
else
{
k=k*2+1;
I/=2;
}
printf("%d\n",k);
}
return 0;
}
From:《算法竞赛入门经典》