撬开约瑟夫的棺材板!

· · 个人记录

最近遇到了很多种约瑟夫问题呢,刚开始做的时候一点都没思路......敲完代码之后还总是错误......然后约瑟夫就顺理成章地成为我“在世界上最讨厌的人”啦!

所以,我我我写这篇博客主要是想讲讲约瑟夫问题以及他的乱七八糟的变式啦!和我一起撬开约瑟夫的棺材板吧!???和上面有什么因果关系吗?

问题来源

要带入一个故事才有趣嘛

据说著名犹太历史学家 Josephus有过以下的故事:

在罗马人占领乔塔帕特后,39 个犹太人与Josephus及他的朋友躲到一个洞中,39个犹太人决定宁愿死也不要被敌人抓到,于是决定了一个自杀方式:

41个人排成一个圆圈,由第1个人开始报数,每报数到第3人该人就必须自杀,然后再由下一个重新报数,直到所有人都自杀身亡为止。

然而Josephus 和他的朋友并不想遵从。

首先从一个人开始,越过k-2个人(因为第一个人已经被越过),并杀掉第k个人。接着,再越过k-1个人,并杀掉第k个人。这个过程沿着圆圈一直进行,直到最终只剩下一个人留下,这个人就可以继续活着。

问题是,给定了和,一开始要站在什么地方才能避免被处决?

Josephus要他的朋友先假装遵从,他将朋友与自己安排在第16和第31个位置,于是逃过了这场死亡游戏。

(摘自搜狗百科:约瑟夫问题·词条)

经典原题

我也不知道是不是原题反正是我遇到的第一个约瑟夫问题

题目描述

有m个人,其编号分别为1~m。按顺序围成一个圈,现在给定一个数n,从第一个人开始依次报数,报到n的人出圈,然后再从下一个人开始,继续从1开始依次报数,报到n的人再出圈,……如此循环,直到最后一个人出圈为止。

编程输出所有人出圈的顺序。

输入

一行两个正整数m和n,之间用一个空格隔开,1<=m<100,1<=n<=32767。

输出

输出m行,每行一个正整数,表示依次出圈的人的编号。

先想象一下画面——一堆人围着篝火跳舞......

如果圈不是那么容易想象的话,就想象一条队伍:从前往后轮流报数,报到目标数字的人出列【但是后面的人不上去,位置留着,是空的】。等所有人都轮完之后再从头开始,跳过空位置......直到队伍里没人。

下面是代码:

#include<bits/stdc++.h>
using namespace std;
int main()
{
    int m,n,i,j,t;//m:人数  n:死(出)亡(圈)数字  
    bool p[100];//p[]判断数字对应的人是否已经出圈 
    cin>>m>>n;
    for(int i=0;i<100;i++)
    {
        p[i]=true;//初始化 
    }
    t=m;//t:圈内剩余人数【刚开始等于m 
    i=0;//i: 类似于指针的东西,现在是第几个报数 
    j=0;//j: 计数器。现在报到第几个数了 
    while(t>0)//如果圈中还有人 
    {
        i++;//第i个位置的人准备开始报数啦【如果i还在的话 
        if(i==m+1)//形成一个圈 
        {
            i=1;
        }
        if(p[i])//如果i位置的人还在的话 
        {
            j++;//i位置的人报数 
            if(j==n)//报到目标数字了吗? 
            {
                cout<<i<<endl;//哦报到了啊,那就出去吧hhh 
                p[i]=false;//既然出去了,那么你这个位置就没人了,为false 
                j=0;//计数器清空 
                t--;//人数减少 
            }
        }
    }
    return 0;
}

变式·One

谁说没写着约瑟夫就不是约瑟夫啦?

魔法师与扑克牌游戏(magic)

题目描述

魔法师在玩一种扑克牌游戏,n张扑克分别记上1,2,…,n。他打开第一张是1,把它放在一边。然后把最上面的两张一张一张地依次移到最后,打开上面一张刚好是2,再放在一边;然后把上面的3张一张一张移到最后,打开上面一张刚好是3,再放到一边;……如此重复下去,直到打开最后一张是n,放在一边,这时他发现,放在一边的扑克刚好是1,2,…n这样排列的。

请编程输出这些扑克原来是怎么排列的。

输入

1行,一个正整数n。

输出

1行,n个正整数,表示这些扑克牌原来的排列顺序,每两个数之间有一个空格。

这道题虽然是队列中的,但是是不是长得也很像约瑟夫?只不过只是目标数字不断变化而已。

【这道题我还没想出来qwq等我想出来了再解释吧......

变式·Two

约瑟夫也可以是猴子hahaha

猴子选大王

题目描述

有n只猴子围成一圈,从l~n编号,大家决定从中选出一个大王。经过协商,决定选大王的规则为:从编号为1的猴子开始报数,报到k的猴子出圈,然后再从下一只开始继续报1到k…… 最后剩下来的那一只就是大王。要求编程从键盘输人n、k,输出成为大王的猴子编号。

输入

一行两个正整数n和k,2<=11<=1000,2<=k<=10^9。

输出

一行一个正整数,代表猴王的编号。

这道题和原题是最像的了,只不过输出改变了一下。在题库里,考点是结构体。

话不多说直接放代码吧【其实和原题的代码差不多,可看可不看呐

#include<bits/stdc++.h>
using namespace std;
int main()
{
    int m,n,i,j,t;//m:人数  n:出圈数字  
    bool p[100];//p[]判断数字对应的猴子是否已经出圈 
    cin>>m>>n;
    for(int i=0;i<100;i++)
    {
        p[i]=true;//初始化 
    }
    t=m;//t:圈内剩余猴子
    i=0;//i: 类似于指针的东西,现在是第几个报数 
    j=0;//j: 计数器。现在报到第几个数了 
    while(t>1)//如果圈中还有大于一只的猴子 
    {
        i++;//第i个位置的猴子准备开始报数啦【如果猴子还在的话 
        if(i==m+1)//形成一个圈 
        {
            i=1;
        }
        if(p[i])//如果i位置的人还在的话 
        {
            j++;//i位置的猴子报数 
            if(j==n)//报到目标数字了吗? 
            {
                p[i]=false;//既然出去了,那么这个位置就没猴子了,为false 
                j=0;//计数器清空 
                t--;//猴子减少 
            }
        }
    }
    for(int i=1;i<=m;i++)
    {
        if(p[i])
        {
            cout<<i;
        }
     } 
    return 0;
}

【正在施工中......先挖个坑......有时间再填