约瑟夫环问题属于数学问题,大概意思是这样的:一群人围成一圈,编号1,2,3……N,给定一个间隔数M,从1开始报数,报到M的人退出,然后余下的人再从1开始报数,报到M的人再退出,求最后留下那个人的编号。
分析:约瑟夫环问题如果在时间允许的条件下,完全可以用程序模拟这个过程,不过我第一次还是没写出来。。
关键是 每次往下数的时候,要判断标号对应的人有没有退出,然后才能往下数,而且计数也只能一次自增1,不能一下子加上M,用bool值判断是否退出,初始值为true,一旦退出的话记为false
#include <iostream>
using namespace std;
int main(){
int n; //表示总人数
int m; //表示间隔
cin>>n;
cin>>m;
bool *p = new bool[n+1];
for(int i=1;i<=n;i++){
*(p+i) = true; //表示任何人都没有退出
}
int count = 0; //记录退出的人数
int i,j;
for(i=1,j=0;;i++){ //i表示循环次数,j表示是不是第N个人
if(*(p+i)){
j++;
if(j == m){
*(p+i) = false;
j = 0;
count++;
}
if(count == n){ //表示已经达到最后一个人
cout<<i<<endl;
break;
}
}
if(i == n) //达到边界时,从头开始计数
i = 0;
}
delete []p;
return 0;
}
这里如果采用一种数学规律来做的话,时间复杂度可以降到O(n)
利用递推式子:
F(2) = F(1) + M
F(3) = F(2) + M
……
F(i) = F(i-1) + M
当只有一个人的时候,标号0的人被输出,注意用这种方法的话,标号是从0开始的,那么输出的结果就必须加1
#include <iostream>
using namespace std;
int main(){
int n,m;
cin>>n>>m;
int result = 0; //当剩下一个人的时候,编号为0的输出
for(int i=2;i<=n;i++){
result = (result + m)%i;
}
cout<<"最后退出的人是"<<result+1<<endl;
return 0;
}