约瑟夫环是一个数学的应用问题:
已知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]; }