约瑟夫问题

[## 约瑟夫问题]

(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;
}

用队列可以方便许多

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值