一群小孩围成一圈,任意假定一个数n,从第一个小孩起,顺时针方向数,每数到第m个小孩时,该小孩便离开。小孩不断离开,圈子不断缩小。最后剩下的一个小孩便是胜者。求胜者的编号?
/************************************
// 函数名称: josephus
// 作 成 者:Erick.Wang
// 作成日期:2016/07/19
// 返 回 值: void
// 参 数: m,n
// 函数说明:用于解决约瑟夫问题
1.对于1……N的数据,声明一个2N的数组;
2.定义两个指针cur和tail,分别用于标记当前访问的数据和最后一个数据;
3.每访问过一个都将其放置到数组最后位置,当cur的访问次数是M的倍数时,
将该位置的数据标记为剔除状态;
4.当tail指针满的时候将剩余数字前移,继续第3步操作。
//************************************/
#define M 2
#define N 8
int a[N*2 + 1];
void josephus(int m,int n)
{
int step = 0;
int count = 0;
int cur = 1;
int tail = n;
for (int i=1; i<=n; i++){
a[i] = i;
}
while (1){
for (int i=1; i<=n+n; i++){
printf("%d ",a[i]);
}
printf("\n");
if (step == m){
printf("delete %d\n",a[cur]);
a[cur] = -1;
cur ++;
count ++;
step = 0;
}
if (count == n-1){
printf("The last one=%d\n",a[cur]);
break;
}
a[tail + 1] = a[cur];
//a[cur] = 0;
cur ++;
tail ++;
step ++;
if (tail == n*2+1){ //执行移动
int j=1;
for (int i=cur-1; i<tail; i++){
a[j ++] = a[i];
}
cur = 1;
tail = j;
}
}
return;
}
以上方法虽然能解决问题,但是当n和m在成百上千的时候时间消耗也是很大的,所以对此问题先用数学模型进行分析,然后再求解,以下有个更快速的方法,时间复杂度为O(N)。
令f表示i个人玩游戏报m退出最后胜利者的编号,最后的结果自然是f[n]
递推公式
f[1]=0;
f[i]=(f[i-1]+m) mod i; (i>1)
有了这个公式,我们要做的就是从1-n顺序算出f的数值,最后结果是f[n]。因为实际生活中编号总是从1开始,我们输出f[n]+1
由于是逐级递推,不需要保存每个f,程序如下:
#include <iostream>
using namespace std;
const int m = 3;
int main()
{
int n, f = 0;
cin >> n;
if(n<=0) {
cout << 0 << endl;
return 0;
}
for (int i = 1; i <= n; i++)
f = (f + m) % i;
cout << f + 1 << endl;
return 0;
}
参考的资料:
http://blog.youkuaiyun.com/firetoucher/article/details/632838
http://www.cnblogs.com/heqinghui/archive/2012/10/04/2711709.html
http://baike.baidu.com/link?url=vwVPN8BkKiW3aJ7UDI31OuAY6bA0YmxVP5tFKs9j_iUMKxjloEi_Jh4s9Rcl3ubkst99ZAuJkgX6xMMyNm7o8a
很精彩:
http://www.cnblogs.com/void/archive/2011/04/21/2024377.html