问题描述
现有n个老鼠围成一圆圈,有一只猫从编号为1 的位置开始吃老鼠,每次都隔一个老鼠吃,请给出最后一个老鼠的编号?题目要求是任给老鼠数i,输出猫最后吃的老鼠的编号。
这个问题常见的解法是建链表遍历或数组设标志。 用这些方法的都是好同学。。
都知道的事多说无意。 看看我的猫儿是如何抓到这最后一只老鼠的。。
先看源码
int
my_cat(int i)
{
int a = 1;
while(a*2<i) a*=2;
if(a != i) a = (i-a)*2;
return a;
}
源码ok.
1 先说说源码做了什么。
while 循环,将a 置为与老鼠总数 i 的位数相同,以备后用。 这里的位数是指 二进制位数。
if语句 计算 最后老鼠的编号。
看到这里,我也不清楚为啥就算出来了呢? 不过,可以肯定的是,和二进制相关。。。
2 因为所以
将问题转化一下,或许更好理解。
猫想吃老鼠了,命令老鼠们按高矮个排好,报数。 1,2,3,4。。。。。。。。。
单数的向前一步,吃掉。 双数的养起来。
这是第一顿。
日上三杆,猫又饿了。 命令老鼠排队报数。1,2,3,。。。。
这次吃单数双数呢?
猫想了想,上次报的总数的是单数的话,这次就吃双数的; 是双数的话,这次就吃单数的。
于是,猫又吃了一顿。
这是第二顿。
夕阳西下。
猫开始了第三顿,还是老规矩,排队报数,单双数规矩和上一顿一样。
第三顿over.
一顿一顿又一顿。
最后,还剩一只老鼠, 这只最长寿的老鼠一开始排第几?
为了让大家明白这个因为所以,让猫儿再吃一遍。
第一顿, 单数吃掉, 留下的老鼠的编号只有双数,也就是说,编号的二进制的最低位是 0。
(i-a)*2中乘2 后正好最低位是0。
吃完后老鼠的总数是 吃前总数数值右移一位。
第二顿, 留下单数还是双数与前一顿吃之前的总数有关。 是双数就留下双数的,是单数就留下单数的。
前一顿吃之前的总数正好是老鼠的总数。单双数取决于二进制的最低位。
因此,幸存老鼠的编号的倒数第二位 与 老鼠的总数的倒数第一位 相同。
第三顿, 留下单数还是双数与前一顿吃之前的总数有关。 是双数就留下双数的,是单数就留下单数的。
前一顿吃之前的总数是 第一顿吃完后的老鼠总数,单双数取决于二进制的最低位,也就是一开始老鼠总数的倒数第二位。
因此,幸存老鼠的编号的倒数第三位 与 老鼠的总数的倒数第二位 相同。
。。。。。。。
最后一顿之前,此时幸存的老鼠就是最后的一只老鼠了(不要问我为什么)。 它的编号也就已经确定了。
最后一位是0,到数第二位与老鼠的总数的倒数第一位 相同,到数第三位与老鼠的总数的倒数第二位 相同,,,,。
此时,老鼠总数是1,也就是开始时老鼠总数的最高位,程序中的a 。 该位不在长寿老鼠的编号中,所以a = (i-a)*2。
以上方法,对于老鼠总数是 2的幂次时,结果为0。第0只老鼠?? 那就是最后一只了。因为猫是绕着圈吃的。 @。@!!
看着猫儿大吃,我也饿了。。。
不要误会, 我不吃老鼠啊。