用递归解决约瑟夫环问题

问题描述

编号为1-N的N个士兵围坐在一起。从编号为1的士兵开始报数(1,2,3,等这样报)。数到m的士兵被枪毙出列。剩下的士兵再从1开始报。以此循环直到只剩1个士兵为止。求这个士兵的编号。

分析

用链表解决的方法在此不加赘述。重点叙述递归的方法。
假设在某次枪毙过后一共还有n个士兵:
在这里插入图片描述
每个黑点的数字代表在枪毙士兵之后士兵的编号。红色的叉所处的位置为刚刚被枪毙的士兵之前所处的位置。
很容易发现,当只剩1名士兵时,代表该名士兵的黑点上的编号为1。那么如果能够建立枪毙士兵的之前和枪毙士兵之后的所有士兵编号之间的关系,就可以很容易地通过递归的方式计算出最后1名士兵在初始状态(1…N)下的编号。
假设目前有n个士兵,编号为1-n。如下图:
在这里插入图片描述
m和n之间存在2种关系:

  1. m ≤ n m \le n mn:

如下图:
在这里插入图片描述

此时,枪毙士兵之后的新圆圈中编号为new的士兵在原圆圈中的编号old为:
o l d = ( n e w + m − 1 ) % n + 1 old = (new + m - 1) \% n + 1 old=(new+m1)%n+1
之所以是加上(m - 1)而不是直接加上m是因为如果:
o l d = ( n e w + m ) % n old = (new + m) \% n old=(new+m)%n
那么在 m = n − 1 m=n-1 m=n1 n e w = 1 new=1 new=1时, ( n e w + m ) (new + m) (new+m)和n取余之后为0,而编号是从1开始的。

  1. m > n m \gt n m>n

此时,假设图中的黑色箭头绕了士兵组成了的圆圈k圈。则可以将m写成:
m = k × n + ( m % n ) ( k > 0 ) m = k \times n + (m \% n) (k \gt 0) m=k×n+(m%n)(k>0)
根据第1种情况的分析,有:
o l d = ( n e w + m − k × n ) % n + 1 old = (new + m - k \times n)\%n + 1 old=(new+mk×n)%n+1
由于 ( n e w + m − k × n ) % n = = ( n e w + m ) % n (new + m - k \times n)\%n == (new + m)\%n (new+mk×n)%n==(new+m)%n
所以这种情况下的new和old之间的关系和第1种情况相同

综上所述,枪毙士兵之后形成的新圆圈中的任一士兵的编号new与其在旧圆圈中的编号old之间的关系是:
o l d = ( n e w + m − 1 ) % n + 1 old = (new + m - 1) \% n + 1 old=(new+m1)%n+1
由于我们开始又得到了最后剩下的士兵的编号为1,那么很容易写出递归代码如下:

int f(int n, int m){
	// n为目前的士兵数量,m被枪毙士兵的报数
	if(n == 1) return n;
	return (f(n - 1, m) + m - 1) % n + 1;
}
  • 5
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值