码迷,mamicode.com
首页 > 其他好文 > 详细

约瑟夫问题

时间:2015-04-05 09:05:05      阅读:133      评论:0      收藏:0      [点我收藏+]

标签:

最近自学了约瑟夫问题,看了别人写的文章和帖子也是千篇一律,虽然没有错误,但是一些关键的地方总是一笔带过.对像我一样的初学者造成了很大的困扰.
自己也思考了很久,这里说下我的思考过程
当然,在这里我只讲如何快速的求出最后剩下的人.
约瑟夫问题:编号从1-n的n个人围成一圈,从1开始每隔m个人就被干掉一个,问最后剩下的人的编号.(例如当n=5,m=3时,结果是4)
(1)首先讲第一点
我们知道第一个人(编号一定是m%n-1) 出列之后,剩下的n-1个人组成了一个新的约瑟夫环 k  k+1  k+2  ... n-2, n-1, 0, 1, 2, ... k-2并且从k开始报0。
现在我们把他们的编号做一下转换:
k     --> 0
k+1   --> 1
k+2   --> 2
...
...
k-2   --> n-2
k-1   --> n-1
变换后就完完全全成为了(n-1)个人报数的子问题,假如我们知道这个子问题的解:例如x是最终的胜利者,那么根据上面这个表把这个x变回去不刚好就是n个人情况的解吗?
如何知道(n-1)个人报数的问题的解?对,只要知道(n-2)个人的解就行了。(n-2)个人的解呢?当然是先求(n-3)的情况 ---- 这显然就是一个倒推问题!好了,思路出来了,下面写递推公式:令f[i]表示i个人玩游戏报m退出最后胜利者的编号,最后的结果自然是f[n]
递推公式
f[1]=0;
f[i]=(f[i-1]+m)%i;  (i>1)
好了,以上就是我讲了第一点,相信大家也发现了上面那一大段是我复制过来的,先不要向我扔砖头,待我把话说完,我之所以复制是因为其他文章上都有讲到这些,我再讲也没意思,相信大家也基本上都知道我们可以通过递推的公式来解决这个问题。如果不知道,请把上面的那一段再看一遍.
接下来我们来讲第二点(也是本文章的重点,注意阅读时不要把下标和标号搞混),也就是为何f[1]=0;f[i]=(f[i-1]+m)%i; (i>1)递推公式为何是这样的,又是如何推出来的.
(i表示当前剩下的人数,m表示每隔m个人干掉一个,f[i]表示当前i个人中胜者所在位置的下标.)
我们以n=6,m=5来作为例子.(我们每次干掉人后,把剩下人按照第一点中的思想再排为一列)
1 2 3 4 5 6
61 2 3 4
6 1 2 3
1 2 3
1 3
1
当只剩下一个人时,毫无疑问,这个人一定是胜者,他所在位置的下标是0也没问题,至于他原本的序号我们不需要知道,因为递推的最后一次是以1到n顺序排列的,因此下标+1即为序号。
现在我们来讨论下如何递推.
就1 2 3 4 5 6(简称s1)到6 1 2 3 4(简称s2)的过程中来说,我们设s1的人数为i(也就是6),假设s2中胜者的下标为x(当然我们已经知道了标号为1的是胜者),那么x在s1中的下标y是多少呢?很难想到是吧,那我们换个角度来想,s1中为y下标的人(当然,该人不能为已经被干掉的人)到了s2当中下标x是多少呢?这里需要分情况讨论。
我们知道每m个人干掉一个,也就是说s1中下标为m%i-1(之所以%i是因为m可能会大于i)的人会被干掉,也就是说s1中下标为m%i-2的人在s2中的下标为i-2,s1中下标为m%i的人在s2中的下标为0.

然后我们分情况来讨论:

      如果y<(m%i-1),那么他在s2中的下标为i-2-(m%i-2-y),即x=i-m+y,也就是说y=x+m%i-i;

                      如果y>(m%i-1),那么他在s2中的下标为y-m%i,即x=y-m%i,也就是说y=x+m%i;
      合并一下得到y=(x+m%i)%i=(x+m)%i;
      再把y改成f[i],x改成f[i-1],大功告成.f[i]=(f[i-1]+m)%i.

约瑟夫问题

标签:

原文地址:http://blog.csdn.net/zafkiel_nightmare/article/details/44879787

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!