算法----约瑟夫环问题

来源:互联网 发布:免费手机翻牆软件 编辑:程序博客网 时间:2024/05/04 07:16

约瑟夫环是一个数学的应用问题:

已知n个人(以编号0,1,2, ... n-1 分别表示)围坐在一张圆桌周围。从编号为0的人开始报数,数到m的那个人出列;他的下一个人又从1开始报数,数到m的那个人又出列;依此规律重复下去,直到圆桌周围的人全部出列。求最后一个出列的人的序号。。


方法 1:

模拟游戏。利用数组或者循环链表,模拟游戏的进行,直到最后仅仅留下一个数。

复杂度是 O(MN)


方法 2: O(N)

1. 一开始,n 个人: 0  1  2  3   ...     m-1   m   m+1 ... n-1     显然,编号 m-1 的人出列;

                                                             ||

                                                             \/

2.            新的序列:0  1  2  3 ...  m-2        m   m+1 ... n-1     从 编号 m 开始新的一轮游戏,

                                                              ||   因为从编号m开始,把后半段放到前面

                                                              \/         

                                 m   m+1   ...  n-1       0      1      2   ...   m-2

                                                              ||   编号变换 <1>, 右半部分加上 n

                                                              \/

                                 m    m+1  ...   n-1      n     n+1   n+2 ...  n+m-2

                                                              ||  编号变换 <2>, 全部减去 m

                                                              \/

                                 0    1   2   3  .......X .............    n - 2

假设红色数字 X 是 [ 0 1 2 ... n-2] 中最终存留的序号,我们的目标是得到这个编号经过了两次编号变换前的编号 x'。

两次编号变换的逆变换其实就是                   x'  = ( X + m) % n;

因此,我们得到了递推关系式   f [n] = ( f[n-1] + m ) % n;

f[i] ---- 表示 i 个人玩这个游戏,最后存留的编号。

代码如下:

// copyright @ L.J.SHOU Mar.11, 2014// jossef circle#include <iostream>#include <vector>using namespace std;int JosefCircle(int m, int n){  vector<int> f(n+1, 0);  for(int i=2; i<=n; ++i)    f[i] = (f[i-1] + m) % i;  return f[n];}


5 0