基础编程题目集 ——7-28 猴子选大王(约瑟夫环)***

本文探讨了一个经典的约瑟夫环问题,一群猴子通过特定规则选出新猴王的过程。采用数组实现数据结构,详细解析了算法的逻辑结构、初始化、处理及输出过程,展示了如何使用取模运算来解决报数问题。

1 题目要求

一群猴子要选新猴王。新猴王的选择方法是:让N只候选猴子围成一圈,从某位置起顺序编号为1~N号。从第1号开始报数,每轮从1报到3凡报到3的猴子即退出圈子,接着又从紧邻的下一只猴子开始同样的报数。如此不断循环,最后剩下的一只猴子就选为猴王。请问是原来第几号猴子当选猴王?

输入格式:
输入在一行中给一个正整数N(≤1000)。

输出格式:
在一行中输出当选猴王的编号。

2 样例

输入样例:
11

输出样例:
7

3 分析

(1)根据题目中加粗部分的特点,可以看出该题是典型的约瑟夫环 问题,数据结构可采用数组或者: 循环链表 实现。我用的是数组。
(2)逻辑结构——从整体到部分:

  • 数组初始化
  • 数组处理
  • 数组输出

(3)数组初始化 state[n]
数组的长度是猴子的个数,数组中每个元素保存猴子的状态(出圈还是在圈) 0,1
状态变化只有 1—>0一种,在数据处理过程中满足 报数%3=0时状态发生变化。
每只猴子最初都在圈中,所以初值都是1.

(4) 数组处理
① 外循环结束条件——
最终只输出一只猴子的编号,即最后只有一只猴子仍然在圈中,其他候选猴子都出圈了。那么据此,整个报数循环的终止条件是 在圈猴子个数=1, 即出圈猴子个数=猴子总个数-1。使用 count实时记录出圈的猴子个数。

2)内循环遍历数组,每次报数的是仍然在圈中的猴子state[i]=1
,每次报的数不超过3 ,采用: 取模运算.,报的数%3=0时,

  • 修改猴子状态为出圈 1—>0
  • 同时更新出圈的猴子的个数 outCount

注意 outCountcycleCount在整个过程中没有清零过。

4 代码

int monkeyKing(int n)
{
// 猴子状态数组 初始化
  int state[n];
  for(int i=0;i<n;i++)
  {
    state[i]=1;
  }
  int cycleCount=0,cycleValue=3,outCount=0;
  // 猴子状态数组 处理
  //如果退出圈子的猴子个数不等于猴子总数-1
  //只剩下一个人时,停止循环
  while(outCount!=(n-1))
  {
    for(int i=0;i<n;i++)
  	{
  	    //考察圈内的猴子
      if(state[i]!=0)
      {
          // 更新它的报数 1 2 0
        cycleCount=(cycleCount+1)%cycleValue;
 
        //如果报数是0,出圈儿  3k%3==0
        if(cycleCount==0)
        {
            state[i]=0;
            //更新出圈猴子的个数
            outCount++;
        }
      }
  	}
  }
  /*猴子状态数组输出*/
    for(int i=0;i<n;i++)
    {
      if (state[i]==1)
        printf("%d",i+1);
    }
  return 0;
}

5 总结

(1)约瑟夫环
(2) 终止条件
(3)取模运算的使用
(4)哪些变量在每次循环中不请0,

6 拓展*

递推式解法

#include "stdafx.h" #include using namespace std; //声明结构:作为链表的节点 struct Monkey { //数据域 int Num = 0; //指针域 Monkey *Mk = NULL; }; //声明子函数:实现生成链表,k为个数 Monkey *Create(int k); //子函数声明:链表输出; void Output(Monkey *Head); //将链表形成循环链表,即tail的指针指向开始位置; void Cricle(Monkey *&Head;,int n); //声明子函数实现循环链表例按照报数为m的出列剩下最后一个大王; void Check(Monkey *Head, int m); int main() { //声明头指针,作为链表的头; Monkey *head = NULL; int n = 8;//猴子个数 int m = 3;//规则数:报到3的猴子出列取消猴王权利; //①生成节点为8的链表; head = Create(n); //②输出结果检查; Output(head); //③将链表形成循环链表; Cricle(head, n); //Output(head);//检验是否形成了循环链表; //④猴王举,并输出猴王; Check(head, m); return 0; } //编译生成链表子函数,这里采用*Ge,Ge和形参指向相同的地址,修改后面的有效; //另外Ge可以修改为别的地址,而不影响形参的指向。 Monkey *Create(int k) { //猴子数为零; if (k == 0) return NULL; //猴子数不为零; Monkey *p = NULL, *q = NULL; Monkey *Head = NULL;//用于储存待返回的表头; //先建立一个; p = new Monkey; p->Num = 1; Head = p; for (int i = 2; i Num = i; p->Mk = q; p = q; } return Head; } //子函数编译:输出链表结构; void Output(Monkey *Head) { int i = 1;//用于链表结点编号; while (Head != NULL) { cout << "猴子的编号是:" << Head->Num << endl; cout << "这只猴子的指针域值为" << Head->Mk <Mk; i++; } return; } //子函数编译:将链表形成循环链表,即tail的指针指向开始位置; void Cricle(Monkey *&Head;,int n) { Monkey *p = Head; for (int i = 1; i Mk; } //此时p指向第n个节点; p->Mk = Head; } //子函数编译:按照规则数出猴王; void Check(Monkey *Head, int m) { Monkey *p = NULL, *q = NULL; q = Head; while (Head != Head->Mk)//检查是否只剩下一个猴子; { //找出规则数节点,删除节点; for (int i = 1; i Mk; } Head = q->Mk;//将头指针放到下一个要报数的猴子身上; p->Mk = q->Mk;//跳过了要删除的猴子; delete q; q = p->Mk; } //输出举结果; cout << "猴王编号是:" << Head->Num << endl; }
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值