Josephus(约瑟夫环)

第二次考虑这个问题,感觉理解深刻多了!

问题描述: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;

}

原文地址:https://www.cnblogs.com/chenbjin/p/Josephus.html