题目示例:
一堆猴子有m个,编号分别是1,2,3 …m,这m个猴子按照编号1,2,…,m的顺序围坐一圈,然后从第1开始数,每数到第n个,该猴子就要离开此圈,这样依次下来,直到圈中只剩下最后一只猴子,则该猴子就为大王。
要求:m和n从键盘输入,分别采用向量及链表两种存储方式实现该问题的求解。
数组解决方案:
·在一个数组中,数组中用1表示猴子在圈中,用0表示猴子已经出圈,数组下标对应与猴子编号对应(例如数组元素p[0]值为1,表示第1只猴子尚在圈中,即p[i]代表编号为i+1的猴子是否在圈中)。
·一只猴子出圈,则将对应的数组值置为0;在报数过程中,要略过值为0的猴子。
#include <stdio.h>
#include <stdlib.h>
#define maxsize 100
void king(int m,int n)
{
int p[maxsize],q[maxsize];
int i,j,t;
t=-1;//t为报数的位置,第一只猴子在位置0(每步需要先加一)
for(i=0;i<m;i++)
{
p[i]=1;//数组初始化,未出圈猴子位置记为1
}
printf("猴子出圈顺序依次为:\n");
for(i=1;i<=m;i++)
{
j=1;//报数用
while(j<=n)
{
t=(t+1)%m;//当报数为报至需要出圈时,报数位置加一
// 因为报数位可能会超过m,故取余回到开头
if(p[t]==1)j++;//当所在位置非空时,报数加一
}
p[t]=0;//已经报数了n个,t处猴子出圈
printf("%d\n",t+1);//因为数组从零记,实际位置需要加一
q[i]=t+1;//将每次出圈的猴子标号记录下来,用于最后得出王者
}
printf("王者为%d",q[m]);
}
int main()
{
int m,n;
printf("请输入猴子个数(100以内)m和每出圈一个需要报的个数n\n");
scanf("%d,%d",&m,&n);
king(m,n);
return 0;
}
链表解决方案
·构建一个循环链表,出圈猴子对应结点删除,当链表中只有一个结点时结束,猴王选出
#include <stdio.h>
#include <stdlib.h>
typedef struct node //定义链表节点类型
{
int data;
struct node*next;
}linklist;
void creat(int n,int m)
{
linklist *head,*p,*s,*q;
int i,total;
/*创建循环链表,头节点也存储信息*/
head=(linklist*)malloc(sizeof(linklist));
p=head;
p->data=1;
p->next=p;
/*初始化循环链表*/
for(i=2;i<=m;i++)
{
s=(linklist*)malloc(sizeof(linklist));
s->data=i;
s->next=p->next;
p->next=s;
p=p->next;
}
p=head;
/*保存节点总数*/
total=m;
q=head;
while(total!=1) //只剩一个节点时停止循环
{
/* 报数过程,p指向要删除的结点*/
for(i=1;i<n;i++)
{
p=p->next;
}
printf("%d\n",p->data);//输出要出圈的猴子序号
/*q指向p结点的前驱 */
while(q->next!=p)
{
q=q->next;
}
/* 删除p结点*/
q->next=p->next;
/* 保存被删除的结点指针*/
s=p;
/* p指向被删除结点的后继*/
p=p->next;
/* 释放被删除的结点*/
free(s);
/* 结点个数减一*/
total--;
}
int king;
king=p->data;
free(p);
printf("king is %d\n",king);
return 0;
}
知识铺垫延伸:
约瑟夫环问题:
N个人围成一圈,从第一个开始报数,第M个将被杀掉,最后剩下一个,其余人都将被杀掉。