第二次考虑这个问题,感觉理解深刻多了!
问题描述:n个人(编号1~n)围成一个环,先淘汰m,再从m的下一位开始报数,报到k的人退出,剩下的人又从淘汰的人下一位开始,从1开始报数....。求胜利者的编号。
例如:n=8,k=5,m=3
则游戏过程为: 12345678 (开始)
淘汰3 新环:4567812 开始从1报数,报到5(k = 5)者被淘汰
淘汰8 新环:124567 报数
淘汰6 新环:71245 报数
淘汰5 新环:7124 报数
淘汰7 新环:124 报数
淘汰2 新环:41 报数
淘汰4 新环:1 胜利者
-----------------------------------------------------------------------------------
解题思路:第一个人m出列后,剩下的人组成一个新环
m+1 m+2 m+3 ... n-1, n, 1, 2 ,... m-1 (做编号转换为 1 2 3 ..... n-1)
我们知道接下来第二个人(编号一定是 k % n-1) 出列之后,剩下的n-2个人又组成了一个新的约瑟夫环(以编号为k+1的人开始):
k+1 k+2 ... n-2,n-1,1,2,... k-1
作编号转换:
k+1 --> 1
k+2 --> 2
..........
k-3 --> n-4
k-2 --> n-3
k-1--->n-2
变换后就成为了(n-2)个人报数的子问题,那么如果我们知道这个子问题的解:例如x 是最终的胜利者,根据编号转换的原则,就可以逆推出 x 在原序列中的位置。
易推出 x'= (x+k) % n-1。 //第一轮m已被淘汰
由此可得递推公式f[i]表示胜利者在 i 个人中的位置:
f[1]=1; //最后的胜利者位于1号位
f[i]=(f[i-1]+k)%i; ( n > i >1) //第一轮m已被淘汰
以上例子的递推过程:
新环:1 胜者位置:f[1] = 1
新环:41 位置:f[2] = 2 =(1+5)% 2(倒数第二轮只有两人)
新环:124 位置:f[3] = 1 = (2+5)% 3
新环:7124 位置:f[4] = 2 = (1+5)% 4
新环:71245 位置:f[5] = 2 = (2+5)% 5
新环:124567 位置:f[6] = 1 = (2+5)% 6
新环:4567812 位置:f[7] = 6 = (1+5)% 7
再做一个处理即可得胜利者原来的位置为:f[8] = (6+3)% 8 = 1
代码:
int Josephus_circle(int n,int k,int m)
{
int ans = 1;
for(int i = 2; i < n; i++)
{
ans = ( ans + k )% i;
if(ans == 0) ans = i; //刚好整除,说明刚好处于i号位
}
ans = (ans + m)%n;
ans = (ans == 0)? n:ans;
return ans;
}