约瑟夫环问题

                                                                             约瑟夫环问题

 

约瑟夫环问题的来源:

        约瑟夫环问题(Josephus)问题是由古罗马史学家约瑟夫(Flavius Josephus)提出的,传说在公元66-70年的犹太人反抗古罗马起义的战争中,约瑟夫作为一名将军,想方设法守住了裘达伯特城长达47天之久,在城市沦陷之后,他和40名死硬的将士在附近的一个洞穴中避难。在洞穴里,这些将士坚持地表决说“要投降毋宁死”。于是,约瑟夫建议每隔两个人杀死一人,而这个顺序是由抽签决定的。约瑟夫有预谋地抓到了最后一签,并且,作为洞穴中的两个幸存者之一,他说服了另一个幸存者一起投降了罗马。

 

约瑟夫问题的规则如下:

(1)n个人围成一个约瑟夫环;

(2)顺时针报数,每次报到m的人将会被淘汰,离开这个约瑟夫环;

(3) 剩下的人又重新组成一个新的约瑟夫环;

(4)然后从被淘汰的下一个人开始重新报数,报到m的人继续被淘汰,离开这个约瑟夫环,以此类推,直到剩余一人。

你非常不幸地参加了这场“游戏”,当然,你是想获胜的,所以你必须快速决定要站到哪一个位置,才能使得最后获胜的人是你。

 

正常想法:

        如何知道n个人中最后剩下的人是谁,如果利用程序实现的话,很多人首先会想到这个圆圈其实就是循环队列,每次数到m的人弹出这个队列,然后剩下的人又重新组成一个新的循环队列,但是这种做法的时间复杂度是O(nm),如果n和m很大的话,这种做法很显然就会超时。

 

正确想法:

        我们要利用约瑟夫问题的性质,即每次数到m的人离开这个约瑟夫环,问题就转化成n-1的约瑟夫环问题了,即m+1...n和1...m-1组成一个新的n-1的约瑟夫环,问题就转化为求n-1的约瑟夫问题了,然后n-1的约瑟夫环问题其实可以转化成人数更少的约瑟夫环问题了,最后人数变成1的时候即是答案了。

        认真地分析过这个过程后,其实有点发现这个过程有点像递归,但是这个问题如果用递归去实现的话比较麻烦,然而一些递归问题是可以用递推去实现的,这一题也不例外。

        递推的实现方法其实就是,为了方便我们进行运算,我们将n个人进行编号,记为0到n-1,这是为了方便后面进行的膜运算,当n为1时,答案自然就是编号为0的人,当n为2时,编号为(m-1)%n的人将被淘汰,剩下的人就是答案,拓展开来,当人数n为任意一个自然数时,第一轮编号为(m-1)%n的人一样被淘汰,然后将编号为(m-1)%n这个人调到约瑟夫环的最前面,编号为(m-1)%n前面的人按顺序调到最后面。

        举个例子,当n为5,m为3时,0 1 2 3 4,第一轮编号为2的人淘汰,剩下的人排列顺序就变成这样:3 4 0 1,可以发现最后剩下的人一定在这4个人当中,所以问题就转化为n为4,m为3的约瑟夫环问题,而n为4,m为3的约瑟夫环问题很明显是0 1 2 3的答案,记为a,然后上面3 4 0 1约瑟夫环问题的答案就为:ans=(a+m)%(n-1),以此类推,答案就很显就出来了。

值得注意的是,最后的答案是第几个人,所以要在编号上加一。

 

标准程序:

#include <cstdio>
int n,m,ans;
int main()
{
	scanf("%d%d",&n,&m);
	for (int i=2;i<=n;i++) ans=(ans+m)%i;
	printf("%d\n",ans+1);
}

 

  • 14
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值