起源
约瑟夫环问题的起源来自犹太历史学家约瑟夫和他的朋友以及39其余的犹太人,总共41人为了躲避敌人,藏在一个山洞中,
39个犹太人决定宁愿死也不被敌人抓到,于是决定自杀,所有人排成一个圈,由第一个人开始报数,每当数到3,就自杀。
这个游戏接着从自杀的位置开始,还是从1数到3。依次类推,约瑟夫将朋友和自己安排在了16和31的位置,最后顺利逃过了
自杀这一劫,因为最后就剩他一个人了。
题目描述
n 个人围成一圈,从第一个人开始报数,数到 k 的人出列,再由下一个人重新从 1 开始报数,数到 k 的人再出圈,依次类推,直到所有的人都出圈,请输出依次出圈人的编号。
输入
输入两个整数,n和m
输出
输出一行 n 个整数,按顺序输出每个出圈人的编号。
传送门
思路1
我们可以用循环数组实现,数到指定的数过后,就标记为已出圈并且统计出圈人数,直到出圈人数等于所有人数。
代码
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
int arr[105];
bool vis[105];
int main(){
int n,k;
scanf("%d%d",&n,&k);
int i=0,j=0,num=0,cnt=0;
while(true){
j=i%n+1;
if(!vis[j]) num++;//没走过就报数
if(num==k){//当报的数为指定值时,则标记为已出圈
num=0;
cnt++;
vis[j]=true;
printf("%d ",j);
if(cnt==n) break;
}
i++;
}
return 0;
}
思路2
我们也可以把这个题想象为是一个队列操作,先把每个数压入队列,然后从队头开始遍历,如果报数等于指定数,则弹出队列,否则压入这个数(压入则是在队尾),再弹出这个数,如果这里不理解,手画一遍,就能明白。
代码
#include<cstdio>
#include<queue>
#include<iostream>
#include<algorithm>
using namespace std;
int main(){
queue<int>q;
int n,k,num=1;
cin>>n>>k;
for(int i=1;i<=n;i++) q.push(i);
while(!q.empty()){
if(num==k){
cout<<q.front()<<" ";
q.pop();
num=1;
}
else{
q.push(q.front());
q.pop();
num++;
}
}
return 0;
}
当然如果题目求最后一个被杀死的人,我们还有效率更高,更巧妙地算法,链接在这里约瑟夫环