1.猴子大王
有 n 只猴子围成一圈,编号为 1~n,打算从中选出一个大王。经过协商,决定选大王的规则如下:从第一只猴子开始循环报数,数到 k 的猴子出圈,然后从下一只猴子继续报数出圈……最后剩下来的那只猴子就是大王。
inputs
一行两个正整数 n 和 k,之间用一个空格分开,2≤n≤1000,2≤k≤10^9 。
6 4
outputs
一行 n 个正整数,表示 n 只猴子依次出圈的编号,中间用一个空格隔开
4 2 1 3 6 5
代码思路
1.由于到末尾需要从头开始,所以将数据存入成环的链表中
2.数数直到k,记录此时的p以及它的前一个结点pre,将p剔除环,并释放内存
#include <iostream>
#include <bits/stdc++.h>
using namespace std;
typedef struct node
{
int data;
struct node *next;
} Node;
int main()
{
Node *Head,*p,*pre,*pTail;
int n,i,n2,j,k;
while(cin>>n>>k)
{
i=1;
n2=n;
Head=(Node*)malloc(sizeof(Node));
Head->data=i++;
n--;//给Head赋值一次总数-1
pTail=Head;//将2-n个数存入链表
while(n--)
{
p=(Node *)malloc(sizeof(Node));
p->data=i++;
pTail->next=p;
pTail=p;
}
pTail->next=Head;
p=Head;
while(n2>0)
{
for(j=1;j!=k;j++)//记录p、pre
{
pre=p;
p=p->next;
}
(n2==1)?printf("%d\n",p->data):printf("%d ",p->data);
n2--;
pre->next=p->next;//剔除结点p
free(p);
p=pre->next;
}
}
return 0;
}
2.猴子大王(进阶)
将 N 个人排成一排,编号 1~N。从第 1 人开始进行 1~M 正向报数,报到 M 的人出列,再从下一个人开始继续1~M 报数、出列。(注意:按某个方向报数报到尾部时,再反方向继续报数)。如此进行下去,直到剩下一人为止,输出最后一个人的编号。
inputs
一行两个正整数 N 和 M,2≤N,M≤300,M≤N,两个数之间用一个空格分隔。
9 3
outputs
一行一个正整数,表示小明在队列中的编号。
8
代码思路
(1)由于需要正向、反向查数,所以需要双向链表,d=1/d=2控制正向、反向
(2)删除结点时,如果删除了头节点,需要重新设置头节点 Head=p->next; 如果删除了尾结点,需要重新设置尾节点 pTail=p->pre; 直至剩下头尾两个结点。
(3)当知剩下头尾两个结点时,根据p的指向和d的值会产生4种情况,删除一个后,Head->data为答案。
注意:双向链表中每个节点都需要考虑当前p的前一个pre的指向、后一个next的指向
#include <iostream>
#include <bits/stdc++.h>
using namespace std;
typedef struct node
{
int data;
struct node *next;
struct node *pre;
} Node;
int main()
{
Node *Head,*p,*q,*pTail;
int n,k,i,d,num;
cin>>n>>k;
//创建双向链表
i=1;
Head=(Node*)malloc(sizeof(Node));
Head->data=i++;
Head->pre=NULL;
Head->next=NULL;
n--;
q=Head;
while(n--)
{
p=(Node *)malloc(sizeof(Node));
p->pre=NULL;
p->next=NULL;
p->data=i++;
q->next=p;
p->pre=q;
q=p;
}
pTail=q;//设置尾结点
p=Head;
d=1;
num=0;
while(Head!=pTail)//直到剩下头尾两个节点
{
num++;
if(num==k)
{
num=0;
if(p==pTail)//删尾,改变尾结点的位置
{
pTail=p->pre;
p=pTail;
d=2;
continue;
}
if(p==Head)//该删头,改变头结点的位置
{
Head=p->next;
p=Head;
d=1;
continue;
}
p->pre->next=p->next;//删除其他节点
p->next->pre=p->pre;
if(d==1)
p=p->next;
else p=p->pre;
continue;
}
if(d==1)//只剩下头尾两个节点时
{
if(p==pTail)
{
p=p->pre;
d=2;
}
else
p=p->next;
}
else
{
if(p==Head)
{
p=p->next;
d=1;
}
else
p=p->pre;
}
}
printf("%d\n",Head->data);
return 0;
}