题目
17世纪法国数学家加斯帕在《数学的游戏问题》中讲的一个故事:n个教徒和n个非教徒在深海上遇险,必须将一半的人投入海中,其余的人才能幸免于难,于是想了个办法:2n个人围成一个圆圈,从第一个人开始依次循环报数,每数到第九个人就将他扔入大海,如此循环直到仅剩n个人为止 。问怎样的排法,才能使每次投入大海的都是非教徒。
输入的形式和输入值的范围
【输入】输入文件由一行构成,就是n的值。
输出的形式
【输出】输出文件中是一行字符串,字符串由n个‘@’字符(代表教徒)和n个‘+’
字符(代表非教徒)排列构成。该排列使得按照前面的约定每次投入大海的都是非教徒。
测试数据
【输入范例】
15
【输出范例】
@@@@+++++@@+@@@+@++@@+++@++@@+
解决代码(使用链表解决该题):
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#define N 100
typedef struct Node//定义一个循环链表
{
char data;
int data1;
struct Node* next;
}Node,*LinkList;
void InitClist(LinkList* CL)//初始化一个循环链表
{
*CL = (LinkList)malloc(sizeof(Node));
(*CL)->next = *CL;
}
void CreatClist(LinkList CL,int n)//在循环链表中不断插入元素
{
Node *rear, * s[N];
rear = CL;//定义的指针指向这个循环链表
for (int i = 0; i < 2 * n; i++)//在循环链表中不断插入新的节点
{
s[i] = (Node*)malloc(sizeof(Node));
rear->next = s[i];
s[i]->data1 = i;
rear=s[i];
}
rear->next = CL;//将循环链表的末尾链接至开头
}
int main()
{
int n,temp=0,k=0,ly=0;
char array[N];
LinkList L;
Node* s;
InitClist(&L);//初始化新循环链表
scanf("%d", &n);//输入非教徒人数,非教徒人数=教徒人数
int length=2*n;//人数为两倍的非教徒人数(教徒+非教徒)
CreatClist(L, n);//创建链表
while (length != n)//限制条件,最终人数不等于教徒人数
{
for (int j = 0; j <8; j++)//不断循环,指向第八个人
{
L = L->next;
}
s = L->next;//一个指针,指向L的下一个,即第九个人
temp = s->data1;//将第九个人所在位置保存下来
if((temp+9)<(2*n-1))//判断这个人的位置+9是否未超过这些教徒的人数,未超出,L等于s的下一个结点
{
L->next = s->next;
}
else //如果超出了,L应该指向s的下一个的下一个(跳过头结点)
{
L->next = s->next; L=L->next;
}
free(s);//释放s结点
length--;//长度减
array[temp]='+';//让数组的temp位置变成+
}
for (k = 0; k < 2 * n; k++)//数组中,如果其所在位置不为“+”,则将其设为“@”
{
if (array[k] != '+')
array[k]='@';
}
for (k = 0; k < 2 * n; k++)//输出
printf("%c", array[k]);
printf("\n");
return 0;
}
测试结果: