您的位置:首页 > 其它

NYOJ 63 小猴子下落

2012-08-04 11:39 141 查看
法一:
1 #include<stdio.h>
#include<stdlib.h>
int main()
{
int d,i,k;
while(~scanf("%d%d",&d,&i)&&(d||i))
{
k=1;
while(--d)
{
if(i&1)
{k*=2;  i=(i+1)/2;}
else
{k=2*k+1;  i/=2;}
}
printf("%d\n",k);
}
system("pause");
return 0;
}


法二:
1 #include<stdio.h>
int main()
{
int i,value,depth,base,newValue;
while(scanf("%d%d",&depth,&value)&&depth||value)
{
for(i=base=1;i<depth;i++)
base<<=1;
for(value--,newValue=i=0;i<depth-1;i++)
{
newValue=(newValue<<1)+(value&1);
value>>=1;
}
printf("%d\n",newValue+base);
}
return 0;
}
本题我是突然想到赫夫曼编码,然后根据其意在题意所描述的满二叉树上编号,结果发现所得的二进制编码,除去最高位只保留深度数减一位,然后化为十进制再加一,就是在这一层上叶子个数的序号,之后再加上这层之前所有的结点个数就是该叶子总的序号,也即是最后一只猴子所到的位置。而且最初得到的二进制编码,恰恰是最后那只猴子的序号减一所对应的二进制数,注意所有的二进制编码位数都是深度减一!循环中用位运算效率较高!


算法分析:刚看这题我就觉得这种题应该会有规律,想了一会还真有。令 a 为第 a 个出去的猴子; 如果 a 为偶数, 说明 a 所在的节点(设ans为 a 所在节点的值) 的开关是开着的,往右走 ans=ans*2+1 , a=a/2 ; 如果 a 为基数 则往左走 ans=ans*2 ,a=a/2+1.

思路:每个小球都会落在根节点上,因为前两个小球必是一个在左字数,一个在右子树。一般的,只需看小球编号的奇偶性,就能指导它是最终在哪棵子树中。对于那些落入根结点左子树的小球来说,只需知道小球是第几个落在根是左子树里面的,就可以知道它下一步是往左还是往右了。依此类推,直至小球落在叶子上。
如果使用题目给出的I,当I是奇数时,它是往左走的第(I+1)/2个小球;当I是偶数时,它是往右走的第I/2个小球。这样可以模拟最后一个小球的路线。

解题思路:
很久前做过这道题,是用的模拟法做的。因为这道题数目的测试数据比较少,所以暴力就过了。
但是如果测试数据很大时,超时是明显的。因为D<=20,所以需要遍历的为2的19次方乘以19,如果有1000组测试数据,就肯定挂了。
今天看到了一种优化,非常巧妙。
因为每个小猴子都是从根节点向下,它必然有两种选择:左、右。而且有规律,它前面的两个猴子一定是左,右。所以在每个节点进入根节点时,我们只需要判断这个点的奇偶性就知道它的方向了。然后它进入根节点的下一层,依然有两种选择,同样的判断,但是数据规模减少一半(每进入1层,舍弃另一个子树,必然减少一半,因为左右的个数相同或者相差1),这样,我们只需要判断这个猴子是第几个进入该层,然后判断奇偶即可。

今天在看二叉树,忽然发现有个题是跟二叉树扯点边,就顺手把它做了。回过头来看这个问题,会发现其实这个题真的很简单。当初之所以没有做而是放到了现在,就是自己犯了想当然的错误,认为这个题是一个要用到二叉树的题,自己没有看二叉树,肯定做不出来。就把它放到一边了····实践证明,这是一道水题·····一次直接水过。以前想的很麻烦,但是后来发现其实这个题很简单。思路也很清晰。不断的模拟小猴子在各个结点的选择。先对输入的小猴子的m编号进行判断,如果它是奇数,那么它就是第(m+1)/2个小猴子,那么它的编号就是2k;偶数的话就是第m/2个小猴子。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: