题目
n个数字(0,1,…,n-1)形成一个圆圈,从数字0开始,每次从这个圆圈中删除第m个数字(第一个为当前数字本身,第二个为当前数字的下一个数字)。当一个数字删除后,从被删除数字的下一个继续删除第m个数字。求出在这个圆圈中剩下的最后一个数字。
思路:
这个是非常典型的问题,别人都叫他约瑟夫环问题。
(1)比较简单的思路是模拟这个过程,用一个数字去模拟这个过程,每次都删掉一个数据,或者是记录一下。
(2)数学归纳法,或者叫做动态规划的东西来做,
(2):数学归纳法:
首先,第一个被删除的数据假设为k=(m-1)%n, 那么删除后数据为 0,1,2······k-1,k+1,k+2······n-1;
然后我们会重新从k+1开始计数,开始数。那么我们是不是可以讲数据重新排序一下:
k+1,k+2······n-1,0,1,2······k-1。
也就是说讲删除后的数据向右边循环移动(n-k-1),也就是说,移动后的序列和原本的序列的对应关系是:
f(x)=(x+n-k-1)%n, x 是原本的位置,发f(x)是计算后的位置。
那么,反过来,f`(x)=(x+k+1)%n。 也就是说移动后映射到移动前的函数是这个。
假设,移动前最后剩下的一个数字的位置函数是f(n,m),移动最后剩下的一个数字位置是f(n-1,m)
那么:f(n,m)=(f(n-1,m)+k+1)%n;
当n=1的时候,显然f(1, m)=0;
所以有:
n=1;f(1,m)=0;
n>1: f(n,m)=(f(n-1,m)+k+1)%n
所以这个就是数学归纳法,根据前面删除一个元素之后的位置,计算出删除元素之前的位置。
而 k=(m-1)%n
所以代入后,可以得到:
n=1;f(1,m)=0;
n>1: f(n,m)=(f(n-1,m)+m)%n
所以代码如下:
int LastRemaining_Solution(int n, int m)
{
if(n<1||m<1)
return -1;
int result=0;
for(int i=2;i<=n;i++){
result=(result+m)%i;
}
return result;
}
参考文献:http://zhedahht.blog.163.com/blog/static/2541117420072250322938/
特例:
当m=2的时候,可以有其他的更快的计算方式,下面的k和上面的k没有关系,下面的k仅仅表示一个正整数
当m=2的时候,并且当 n=2^k的时候,也就是数的个数是2的指数倍的时候
第一轮,从前到后,一共会删除 n/2=2^(k-1)个数,同时0还是会作为计数的起点。这个时候剩下的个数n=2^(k-1)
第二轮,从前到后,删除 n/2=2^(k-2)个数 ,0 还是会作为计数的起点,这个时候还剩下 n=2^(k-2)个。
···
第 k-1轮,这个时候会剩下 n=2^(k-(k-1))=2^1
第 k 轮 只剩下 0 这个最初的起点。
所以,当,m=2,切n=2^k的时候,最后剩下的数一定是0;
- 当,n= 2^k+l < 2^(k+1)的时候怎么办。好办,这个时候第一轮删掉l个数就可以了。这个时候就剩下了 n= 2^k个数了
所以,这个时候删掉l个数后第一个数将会一直在每一次的删数的过程中存活下来,也就是最终的数
这里简单的证明一下,第一轮可以删掉的个数大于l个:
- ∵ n= 2^k+l < 2^(k+1)
- ∴ l<2^k
- 而 第一轮删掉的个数为:n/2=2^(k-1)+l/2;
- ∵ l<2^k => 2^(k-1)>l/2
- ∴ n/2=2^(k-1)+l/2 > l/2 +l/2=l;
- 所以第一轮删掉的元素个数一定是大于 l 个的
综上,当m=2的时候,f(n,m) 表示最终剩下的数,那么有:
n=2^k :f(n,m)= 0;
n=n= 2^k+l:f(n,m)= 2*l;
(n个数的的排列是0,1,2,3,·····n-1)

本文介绍了约瑟夫环问题的解决思路,主要通过数学归纳法来探讨。首先解释了问题背景,然后详细阐述了数学归纳法的解题步骤,包括特殊情况下(m=2)的快速计算方式。最后,总结了当m=2时,对于不同n值的解决方案。
312

被折叠的 条评论
为什么被折叠?



