约瑟夫问题 C++求解

约瑟夫环(Josephus)问题是由古罗马的史学家约瑟夫(Josephus)提出的,其表述方式有多种,较典型的一种是:
有n只猴子,按顺时针方向围成一圈选大王(编号从1到n),从第1号开始报数,一直数到m,数到m的猴子退出圈外,剩下的猴子再接着从1 开始报数。就这样,直到圈内只剩下一只猴子时,这个猴子就是猴王,编程求输入n,m后,输出最后猴王的编号。

  • 解法一:数组实现

根据输入的n值确定数组的长度,对数组中的n个元素依次编号i+1(0≤i<n),然后这n个元素再进行从1到m的报数j(0≤j≤m),每当报到m时(m==j),将数组中对应元素的值置为0,并令j=0,总人数减1(cnt–);该元素后面的一个元素重新从1开始报数(j++)。(由于是数组实现,难以及时将置为0的空间删除,所以在报数前需要检验该元素是否为0(a[i]!=0?)。
当总人数(cnt)减为1时退出while()循环,输出数组中值不为0的元素,即最后留下的元素序号。

#include <iostream>
using namespace std;

int main()
{
    int n,m,cnt;
    cout<<"n=";
    cin>>n;
    cout<<"m=";
    cin>>m;
    int a[n];
    for(int i=0;i<n;i++)
        a[i]=i+1;
    cnt=n;
    int j=0;
    while(cnt>1)
    {
        for(int i=0;i<n;i++)
        {
            if(a[i]!=0)
                j++;
            if(j==m)
            {
                j=0;
                a[i]=0;
                cnt--;
            }
        }
    }
    for(int i=0;i<n;i++)
        if(a[i]!=0)
           cout<<a[i]<<endl;
    return 0;
}
  • 解法二:循环链表实现

创建一个由n个结点(含头结点)构成的循环链表,每个结点的数字域存放1~n的整型值。这n个结点进行1到m的报数,每当报到m时,删除对应的结点(delete temp),并使n–,直到n减为1,退出循环,输出剩下的这个结点的数字域的值,即为该结点的序号。

#include<iostream>
using namespace std;

typedef struct SNode
{
    int data;
    SNode *next;
}SLNode;

main()
{
    int ListJoseph(int n,int m);
    int n,m;
    cout<<"n=";
    cin>>n;
    cout<<"m=";
    cin>>m;
    ListJoseph(n,m);
    return 0;
}

int ListJoseph(int n,int m)
{
    SLNode head,*p,*q;
    head.data=1;  //head为头结点,其数据域存放第一个元素的值,而非数据域为空的头指针
    p=&head;
    int j=2;
    if(n<2)
    {
        cout<<"总人数过少"<<endl;
        return 0;
    }
    while(j<=n)
    {
        /*在链表末尾新增一个结点,并使新增结点的next指针指向头结点*/
        q=new SLNode;
        q->data=j;
        q->next=&head;
        p->next=q;
        p=q;
        j++;
    }
    
    p=&head;
    int key=1;
    while(n>1)
    {
        key++;
        if(key==m)
        {
            /*当key==m时,删除对应的结点(p->next)*/
            SLNode *temp;
            temp=p->next;
            delete temp;
            p->next=p->next->next;
            n--;
            key=1;
        }
        p=p->next;
    }
    cout<<p->data<<endl;
    return 1;
}
  • 解法三:递归实现

在网上查到了用递归解决约瑟夫问题的代码(https://blog.csdn.net/m15682532244/article/details/78304165),代码很简短,但花了好几个小时仍不能完全理解,似乎理解了个大概,但自己无法说清具体是怎么回事。
我炮制的代码如下:

#include <iostream>
using namespace std;

int main()
{
    int Joseph(int n,int m);
    int n,m;
    cout<<"n=";
    cin>>n;
    cout<<"m=";
    cin>>m;
    cout<<Joseph(n,m)<<endl;
    return 0;
}

int Joseph(int n,int m)
{
    if(n==2)
        return((m%n)+1);
    else
        return((Joseph(n-1,m)-1+m)%n+1);
}

我令m=3,对2至11间的n值进行验证,代码是正确的,但其中的道理,是我所不能言说的,只将验证过程附在下面:
(第一行的值是1,2,3的循环,表示下面每一行的报数情况。下面每行最后标红的数字,是最后留下的数字。)
在这里插入图片描述

  • 25
    点赞
  • 156
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值