据说著名犹太历史学家Josephus有过以下的故事:在罗马人占领乔塔帕特后,39 个犹太人与Josephus及他的朋友躲到一个洞中,39个犹太人决定宁愿死也不要被敌人抓到,于是决定了一个自杀方式,41个人排成一个圆圈,由第1个人开始报数,每报数到第3人该人就必须自杀,然后再由下一个重新报数,直到所有人都自杀身亡为止。然而Josephus 和他的朋友并不想遵从。首先从一个人开始,越过k-2个人(因为第一个人已经被越过),并杀掉第k个人。接着,再越过k-1个人,并杀掉第k个人。这个过程沿着圆圈一直进行,直到最终只剩下一个人留下,这个人就可以继续活着。问题是,给定了和,一开始要站在什么地方才能避免被处决。Josephus要他的朋友先假装遵从,他将朋友与自己安排在第16个与第31个位置,于是逃过了这场死亡游戏。
17世纪的法国数学家加斯帕在《数目的游戏问题》中讲了这样一个故事:15个教徒和15 个非教徒在深海上遇险,必须将一半的人投入海中,其余的人才能幸免于难,于是想了一个办法:30个人围成一圆圈,从第一个人开始依次报数,每数到第九个人就将他扔入大海,如此循环进行直到仅余15个人为止。问怎样排法,才能使每次投入大海的都是非教徒。
【问题分析与算法设计】
约瑟夫问题并不难,求解的方法很多;题目的变化形式也很多。这里给出一种实现方法。
题目中30个人围成一圈,因而启发我们用一个循环的链来表示,可以使用结构数组来构成一个循环链。结构中有两个成员,其一为指向下一个人的指针,以构成环形的链;其二为该人是否被扔下海的标记,为0表示还在船上。从第一个人开始对还未扔下海的人进行计数,每数到9时,将结构中的标记改为1,表示该人已被扔下海了。这样循环计数直到有15个人被扔下海为止。
约瑟夫问题是个有名的问题:N个人围成一圈,从第一个开始报数,第M个将被杀掉,最后剩下一个,其余人都将被杀掉。例如N=6,M=5,被杀掉的顺序是:5,4,6,2,3。
假如最后剩下的活的人,也被杀了,那么被杀掉的顺序是:5,4,6,2,3,1。
分析:
(1)由于对于每个人只有死和活两种状态,因此可以用布尔型数组标记每个人的状态,可用true(或者1)表示死,false(或者0)表示活。
(2)开始时每个人都是活的,所以数组初值全部赋为false。
(3)模拟杀人过程,直到所有人都被杀死为止。
约瑟夫经典代码:
#include<bits/stdc++.h>
using namespace std;
int main(){
bool a[101]={0};
int n,m,i,f=0,t=0,s=0;
cin>>n>>m;
while(f!=n){ //直到所有人都被杀死为止
++t;//逐个枚举圈中的所有位置
if(t>n)
t=1;//数组模拟环状,最后一个与第一个相连
if(a[t]==0)
s++;//第t个位置上有人则报数
if(s==m)//当前报的数是m
{
s=0;//计数器清零
cout<<t<<' ';//输出被杀人编号
a[t]=1;//此处人已死,设置a[t]为1
f++;//死亡人数+1
}
}
return 0;
}
参考例题:
有n个正整数放到数组里,现在将数组中,个位数是1的数,变成0,然后从大到小输出新数组。
输入格式
第一行1个正整数:n,范围在[1,100]。
第二行n个不同的正整数:范围在[1,10000]。
输出格式
n个正整数,两个之间用一个空格隔开。
输入/输出例子1
输入:
5
23 541 26 145 627
输出:
627 145 26 23 0
样例解释
无
代码如下:
#include<bits/stdc++.h>
using namespace std;
int n,a[105];
int main(){
cin>>n;
for(int i=1;i<=n;i++)
cin>>a[i];
for(int i=1;i<=n;i++)
{
if(a[i]%10==1)a[i]=0;
}
sort(a+1,a+n+1);
for(int i=n;i>=1;i--)
cout<<a[i]<<" ";
return 0;
}
有n个正整数放到a数组里,现在将a数组赋值给b数组,再将b数组接到a数组的后面,组成一个新数组。
现将新数组,从第一个开始,每间隔m个数的值变成0,输出改变后的数组。
输入格式
第一行2个正整数:n,m,范围都在[1,100]。
第二行n个不同的正整数:范围在[1,1000]。
输出格式
n*2个正整数,两个之间用一个空格隔开。
输入/输出例子1
输入:
5 2
1 2 3 4 5
输出:
0 2 3 0 5 1 0 3 4 0
样例解释
无
代码如下:
#include<bits/stdc++.h>
using namespace std;
int n,m,a[205],b[1005];
int main(){
cin>>n>>m;
for(int i=1;i<=n;i++)
{
cin>>a[i];
a[n+i]=a[i];
}
for(int i=1;i<=n*2;i=i+m+1)
{
a[i]=0;
}
for(int i=1;i<=n*2;i++)
cout<<a[i]<<" ";
return 0;
}
n个人排成一圈。从某个人开始,按顺时针方向依次编号。从编号为1的人开始顺时针"一二……"报数,报到2的人退出圈子。这样不断循环下去,圈子里的人将不断减少。由于人的个数是有限的,因此最终会剩下一个人。试问最后剩下的人最开始的编号。
输入格式
一个正整数n,表示人的个数。输入数据保证数字n<=1000。
输出格式
一个正整数。它表示经过“一二……”报数后最后剩下的人的编号。
输入/输出例子1
输入:
9
输出:
3
样例解释
当n=9时,退出圈子的人的编号依次为:
2 4 6 8 1 5 9 7
最后剩下的人编号为3
代码如下:
#include<bits/stdc++.h>
using namespace std;
bool a[101]={0};
int n,m=2,x=0,t=0,z=0;
int main(){
cin>>n;
while(x!=n)
{
t=t+1;
if(t>n)t=1;
if(a[t]==0)z=z+1;
if(z==m)
{
z=0;
a[t]=1;
x++;
}
}
cout<<t;
return 0;
}
编号为1、2、3、...、N的N个人按顺时针方向围坐一圈,每人持有一个密码(正整数)。从指定编号为1的人开始,按顺时针方向自1开始顺序报数,报到指定数M时停止报数,报M的人出列,并将他的密码作为新的M值,从他在顺时针方向的下一个人开始,重新从1报数,依此类推,直至所有的人全部出列为止。第一个指定的数M,为第一个人所持有的密码。
请设计一个程序求出出列的顺序,其中N,M<=30。
输入格式
第一行1个整数 N,表示人数。
第二行N个整数,表示每人手上的密码值
输出格式
N个数,两个相邻的数字用空格隔开
输入/输出例子1
输入:
3
2 2 2
输出:
2 1 3
输入/输出例子2
输入:
4
3 5 2 3
输出:
3 1 2 4
样例解释
无
代码如下:
#include<bits/stdc++.h>
using namespace std;
bool a[101]={0};
int b[101],n,m,x=0,t=0,z=0;
int main(){
cin>>n;
for(int i=1;i<=n;i++)
cin>>b[i];
m=b[1];
while(x!=n)
{
t=t+1;
if(t>n)t=1;
if(a[t]==0)z=z+1;
if(z==m)
{
z=0;
cout<<t<<" ";
m=b[t];
a[t]=1;
x++;
}
}
return 0;
}
n只猴子要选大王,选举方法如下:所有猴子按1,2,…,n编号并按照顺序围成一圈,从第k个猴子起,由1开始报数,报到m时,该猴子就跳出圈外,下一只猴子再次由1开始报数,如此循环,直到圈内只剩下一只猴子时,这只猴子就是大王。
输入格式
三个整数,猴子总数n(n<1000),起始报数的猴子编号为k,出局数字为m(m<100)
输出格式
猴子大王的编号
输入/输出例子1
输入:
10 5 3
输出:
8
样例解释
无
代码如下:
#include<bits/stdc++.h>
using namespace std;
bool a[1001]={0};
int n,m,k,x=0,t=0,z=0;
int main(){
cin>>n>>k>>m;
t=k-1;
while(x!=n)
{
t=t+1;
if(t>n)t=1;
if(a[t]==0)z=z+1;
if(z==m)
{
z=0;
a[t]=1;
x++;
}
}
cout<<t;
return 0;
}
你一定听说过约瑟夫问题吧?即从N个人中找出唯一的幸存者。现在老约瑟夫将组织一个皆大欢喜的新游戏,假设N个人站成一圈,从第1人开始交替的去掉游戏者,但只是暂时去掉(例如一开始的暂时去掉2),直到最后剩下唯一的幸存者为止。幸存者选出后,所有比幸存者号码高的人每人得到1个金币,永久性离开。其余剩下的将重复以上的游戏过程,比幸存者号码高的人每人得到1个金币后离开。经过这样的过程后,一旦人数不再减少,则最后剩下的那些人将得到2个金币。请你计算一下老约瑟夫一共要付出多少个金币?
输入格式
一行一个正整数N表示人数。
输出格式
一行一个正整数表示共需支付的金币个数。
输入/输出例子1
输入:
10
输出:
13
样例解释
1<=N<=100000
代码如下:
#include<bits/stdc++.h>
using namespace std;
long long n,i=2;
long long a[1000005],b[1000005];
int main(){
cin>>n;
a[i]=1,b[1]=1;
while(i<=n)
{
b[i]=(b[i-1]+1)%i+1;
if(b[i]==i)a[i]=i;
else a[i]=a[b[i]];
i++;
}
cout<<a[n]+n;
return 0;
}