约瑟夫环的三种解法
约瑟夫问题是个有名的问题:N个人围成一圈,从第一个开始报数,第M个将被杀掉,最后剩下一个,其余人都将被杀掉。
N个人从1开始编号,问最后活下来的人的编号是多少。
方法1:数组模拟。
#include<bits/stdc++.h>
using namespace std;
int main(){
printf("--------约瑟夫环问题----------\n");
printf("请输入总共人数和报数死亡的编号数:");
int n,m;
cin>>n>>m;
bool a[n+1];
memset(a,0,sizeof(a));
int tot=0,cnt=0,i=0;//当前死亡总人数,报数的报数器.
while(cnt<n){
i++;
if(i>n) i=1;
if(!a[i]) cnt++;
if(cnt==m){
cnt=0;
tot++;
a[i]=1;
printf("第%d个死的人的编号为%d\n",tot,i);
}
}
printf("游戏结束,所有人死亡\n");
return 0;
}
方法2:数学递推
主要是利用递推的思想,令Y[ i ]为有i个人时最后活着的人的编号(从0开始编号)。显然Y[ 1 ]=0,现在我们来考虑Y[2]为多少。我们已知报到 m-1 的会死.
所有当有两个人的时候.死的就是那个报m-1的人.然后编号为m的那个人就活了下来.所以Y[ 2 ] = Y[ 1 ] + m
依次进行递推:可得Y[ i ] = Y[ i-1] + m ,由于我们知道报数类似于一个循环.所以编号可能会超出范围.所以我们对每一个进行取模。 Y[ i ] = ( Y [ i- 1] + m) % i ,对 i 取模 保证 编号是从0到 i -1 。
#include<bits/stdc++.h>
using namespace std;
int main(){
int n,m;
while(cin>>n>>m){
int p=0;
for(int i=2;i<=n;i++){
p=(p+m)%i;// i=2时是n=2时最后活着的人编号(从0开始),i=1时,最后活着的人编号肯定为0
}
printf("最后活着的人的编号为%d\n",p+1);
}
}
方法3:队列实现.
将队伍看成一个循环队列.如果没点到第m个人就放到队尾,否则弹出,cnt重置.下面上代码.
#include<bits/stdc++.h>
using namespace std;
int main(){
queue<int>q;
int n,m;
cin>>n>>m;
for(int i=1;i<=n;i++) q.push(i);
int cnt=1;
while(q.size())
{
if(cnt==m)
{
printf(q.size()==1?"%d\n":"%d ",q.front());
q.pop();
cnt=1;
}
else {
int tmp=q.front();
cnt++;
q.pop();
q.push(tmp);
}
}
return 0;
}