[## 约瑟夫问题]
(https://baike.baidu.com/item/%E7%BA%A6%E7%91%9F%E5%A4%AB%E9%97%AE%E9%A2%98/3857719?fr=aladdin)
*
1.据说著名犹太历史学家 Josephus有过以下的故事:在罗马人占领乔塔帕特后,39 个犹太人与Josephus及他的朋友躲到一个洞中,39个犹太人决定宁愿死也不要被敌人抓到,于是决定了一个自杀方式,41个人排成一个圆圈,由第1个人开始报数,每报数到第3人该人就必须自杀,然后再由下一个重新报数,直到所有人都自杀身亡为止。然而Josephus 和他的朋友并不想遵从。首先从一个人开始,越过k-2个人(因为第一个人已经被越过),并杀掉第k个人。接着,再越过k-1个人,并杀掉第k个人。这个过程沿着圆圈一直进行,直到最终只剩下一个人留下,这个人就可以继续活着。问题是,给定了和,一开始要站在什么地方才能避免被处决?Josephus要他的朋友先假装遵从,他将朋友与自己安排在第16个与第31个位置,于是逃过了这场死亡游戏。
17世纪的法国数学家加斯帕在《数目的游戏问题》中讲了这样一个故事:15个教徒和15 个非教徒在深海上遇险,必须将一半的人投入海中,其余的人才能幸免于难,于是想了一个办法:30个人围成一圆圈,从第一个人开始依次报数,每数到第九个人就将他扔入大海,如此循环进行直到仅余15个人为止。问怎样排法,才能使每次投入大海的都是非教徒。
问题分析与算法设计
约瑟夫问题并不难,但求解的方法很多;题目的变化形式也很多。这里给出一种实现方法。
题目中30个人围成一圈,因而启发我们用一个循环的链来表示,可以使用结构数组来构成一个循环链。结构中有两个成员,其一为指向下一个人的指针,以构成环形的链;其二为该人是否被扔下海的标记,为1表示还在船上。从第一个人开始对还未扔下海的人进行计数,每数到9时,将结构中的标记改为0,表示该人已被扔下海了。这样循环计数直到有15个人被扔下海为止。
解决办法
c++
#include <iostream>
#include <iomanip>
#include <cmath>
using namespace std;
int main()
{
int a[8],i,t,k;
for(i=1;i<=8;i++)
{
a[i]=1;
}
t=8;
k=0;
while(t>0)
{
for(i=1;i<=8;i++)
{
if(a[i]==1)
{
k++;
if(k==5)
{
k=0;
a[i]=0;
cout<<setw(3)<<i;
t--;
if(t==0)
{
break;
}
}
}
}
}
return 0;
}
#include<iostream>
using namespace std;
main()
{
bool a[101]={0};
int n,m,i,f=0,t=0,s=0;
cin>>n>>m;
do
{
++t;//逐个枚举圈中的所有位置
if(t>n)
t=1;//数组模拟环状,最后一个与第一个相连
if(!a[t])
s++;//第t个位置上有人则报数
if(s==m)//当前报的数是m
{
s=0;//计数器清零
cout<<t<<' ';//输出被杀人编号
a[t]=1;//此处人已死,设置为空
f++;//死亡人数+1
}
}while(f!=n);//直到所有人都被杀死为止
}
pascal
var
a:array [1..20] of integer;
n,m,i,j,k,n1,m1:integer;
begin
readln(m,n);
for i:=1 to m do
a[i]:=i;
m1:=m;
n1:=1;
while m1>0 do
begin
j:=(n+n1-1-1) mod m1 +1;
n1:=j;
m1:=m1-1;
writeln(a[j]);
for k:=j to m1 do
a[k]:=a[k+1];
end;
end.
例题
#include <iostream>
#include <bits/stdc++.h>
using namespace std;
queue<int>vis1;
int n,m,x,s1;
int main()
{while(cin>>n>>m)
{for(int i=1;i<=n;i++)
vis1.push(i);
x=0;
while(vis1.size()>1)
{
x++;
s1=vis1.front();
vis1.pop();
if(x%m!=0)
vis1.push(s1);
}
cout<<vis1.front()<<endl;
}
return 0;
}
用队列可以方便许多