猴子选王——约瑟夫环问题

题目

一堆猴子都有编号,编号是1,2,3 …m,这群猴子(m个)按照1-m的顺序围坐一圈,从第1开始数,每数到第n个,该猴子就要离开此圈,这样依次下来,直到圈中只剩下最后一只猴子,则该猴子为大王。

一、解题思路

1.向量解法(数组解法):

第一步 首先将所有猴子进行初步编号(从1到n)4
第二步 因为所有猴子只有两种状态:淘汰与未被淘汰。所以将所有猴子进行状态编
号(0表示未淘汰,1表示被淘汰),先将所有猴子标记为0,表示未淘汰。
第三步:在主函数里开始循环报数淘汰猴子,用另一个计数变量K来统计报数猴子,当k和要淘汰的猴子报的数字相等时,淘汰该猴子,k值清零再次循环,知道猴子剩余数量j等于1时结束循环
第四步:遍历数组,找到状态参数为0的猴子就是猴子王

2.链表解法:

第一步:构建循环环状链表,存放猴子编号
第二步:报数找到淘汰数字前一个猴子,对应就能找到要被淘汰的猴子
第三步:删除淘汰猴子的结点,释放空间
第四步:让被淘汰猴子的下一个结点开始报数,循环此过程
第五步:知道猴子剩余数量为1时候,结束循环,函数返回剩下那个猴子的编号信息。即猴王编号

二、方法优缺点

1.向量法

1)优点:
1.数组能够支持下标访问,访问效率很高;
2.查找速度很快;

2)缺点:
1.数组的大小要提前声明,容易造成大额空间浪费,也容易出现空间不足需要重新定义数组的情况;
2.元素的插入删除也不方便操作,只能使用状态变量来标记猴子;

2.链表法:

1)优点:
1.插入,删除操作很便捷,在结点上操作十分灵活。
2.大小不固定,容易扩展;
3.内存的利用效率也很高,不会浪费内存;

2)缺点:
1.每次遍历数据时只能从头开始,查找效率很低。
2.随机访问效率低

三、流程图

1.向量法(数组法)
在这里插入图片描述
2.链表法
在这里插入图片描述

四、实验代码

#include <stdio.h>
#include <stdlib.h>

#define N 100 //宏定义向量长度
int main()
{
    int a[N],n,m;
    printf("请输入猴子的个数:\n");
    scanf("%d",&m);
    printf("请输入要淘汰的数字:\n");
    scanf("%d",&n);
    int i,j,k;
    k=0;
    j=m;// 计数变量,用来统计剩余猴子数量
    for(i=0; i<m; i++)
        a[i]=0;//对这些猴子进行状态编号,分为0,1,(0状态为正常猴子,1状态是被淘汰猴子)
    if(n==1||m==1)
        printf("此情况无意义");
    else for(i=0;j!=1;)
    {
        if(a[i]==0)
         {
             k++;//用来再一次重头统计猴子数目
         }
        if(k==n)//如果重头编号时候K和要淘汰的数字相等时,该猴子淘汰。k重新计数
        {
            a[i]=1;//该猴子状态改变,变为淘汰态:1
            k=0;//k清零重新计数
            j--;//剩余猴子数减一
        }
        i++;
        if(i==m)//此时最后一个猴子报完数,一圈已经结束。i清零后开始下一圈剩余猴子报数
        {
            i=0;
        }
    }
    for(i=0; i<m; i++)
    {
        if(a[i]==0)//此时还是0状态的猴子就是王
            printf("猴子王是最初的%d号猴子\n",i+1);
    }
    return 0;
}

2.	链表法代码
#include <stdio.h>
#include <stdlib.h>

typedef struct monkey
{
    int data;
    struct monkey *next;
} Monkey;

int create(int m, int n)//m猴子数量,n是要淘汰的数字
{
    Monkey *head,*q,*p,*t;
    head = (Monkey*)malloc(sizeof(Monkey));
    p=head;
    p->data = 1;//存放第一个猴子数据
    p->next = p;//构建循环链表
    for(int i=2 ; i<=m ; i++)
    {
        t=(Monkey*)malloc(sizeof(Monkey));
        t->data=i;
        t->next=p->next;
        p->next=t;
        p=p->next;//这个循环不断申请空间,将存放信息的结点插入链表
    }
    p=head;//形成闭环,循环链表
    q=head;//在后面用来寻找淘汰猴子的前驱
    int j=n;//j用来统计剩余猴子数目
    while(j!=1)//当j等于1,即剩余一个猴子时候结束循环
    {
        for(int i=1; i<n; i++) //报数过程,报到n-1时停止,下一个就是要被淘汰的猴子
        {
            p = p->next;
        }
        while (q->next != p)//让q指向要淘汰猴子的前驱
        {
            q = q->next;
        }
        q->next=p->next;//删除这个淘汰猴子的结点
        p = p->next;//让p从被淘汰猴子的后一位再开始新的报数
        j--;//淘汰一个猴子,总猴子数目减一
    }
    int king = p->data;//返回剩余那个猴子的编号
	free(p); 
    return king;
}

int main()
{
    int t,m,n;
    printf("请输入猴子的个数:\n");
    scanf("%d",&m);
    printf("请输入要淘汰的数字:\n");
    scanf("%d",&n);
    t=create(m,n);
    printf("最后的猴王是最初的第%d个猴子",t);
    return 0;
}

五、运行截图

在这里插入图片描述

  • 16
    点赞
  • 95
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
约瑟夫问题是一个古老且著名的问题,它描述了n个人围成一圈,从第一个人始报数,每次报到第m个人,这个人将被杀掉,直到最后只剩下一个人。 这个问题可以通过模拟来解答。首先,我们创建一个包含n个人的循环链表,每个节点表示一个人。然后,我们从第一个人始,按顺序数m个人,直到找到第m个人。然后,我们将这个人从链表中移除,再次从移除的下一个人始,继续数m个人,一直重复这个过程,直到链表中只剩下一个人。 为了更好地理解,我们可以用一个具体的例子来说明。假设有5个人(编号为1,2,3,4,5)围成一圈,从第一个人始报数,第3个人将被杀掉。 首先,我们从第一个人始,数1,2,3,第3个人是编号为3的人,将其移除。现在剩下4个人:1,2,4,5。接下来,我们从编号为4的人始,数1,2,3,第3个人是编号为2的人,将其移除。现在剩下3个人:1,4,5。我们继续从编号为4的人始,数1,2,3,第3个人是编号为5的人,将其移除。现在剩下2个人:1,4。我们再次从编号为1的人始,数1,2,3,第3个人是编号为1的人,将其移除。最后,只剩下编号为4的人,他是幸存者。 总结来说,约瑟夫问题是一个经典的数学问题,可以通过模拟来解答。每次从围成一圈的人中按顺序数m个人,将第m个人移除,最后只剩下一个人。这个问题具有一定的实际意义,也可以帮助我们理解和运用数学模型。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值