作者 李卫明
单位 杭州电子科技大学
1.7 好玩的约瑟夫环:有M个人,编号分别为1到M,玩约瑟夫环游戏,最初时按编号顺序排成队列;每遍游戏开始时,有一个正整数报数密码N,队列中人依次围坐成一圈,从队首的人开始报数,报到N的人出列,然后再从出列的下一人开始重新报数,报到N的人出列;重复这一过程,直至所有人出列,完成一遍游戏,所有出列的人形成新队列;游戏可能玩很多遍,每遍有新报数密码。求若干遍游戏完成后队列次序。题目输入包括若干个正整数(至少1个),第一个正整数为玩游戏人数M,后续每个正整数为每遍游戏报数密码,报数密码可能为1,题目要求按出队列顺序输出他们的编号。
输入格式:
正整数M和后面的若干个正整数报数。
输出格式:
每个测试用例结果占一行,每个编号占4位。
输入样例:
10 3 5 2
输出样例:
4 6 5 2 9 1 3 7 8 10
代码长度限制
16 KB
时间限制
400 ms
内存限制
64 MB
栈限制
8192 KB
C程序如下:
#include <stdio.h>
#include <stdlib.h>
typedef struct LinkNode {
int Data;
struct LinkNode* Next;
} LinkNode, *LinkList;
LinkList Append(int num); // 构建单向循环链表
void Display(LinkList L); // 输出约瑟夫环
LinkList Newjosephus(LinkList old, int times); // 根据第一次输出的约瑟夫环的值构建约瑟夫环
int main() {
LinkList first, second;//创建两个指针,分别为第一次构建的约瑟夫环和后序的约瑟夫环
int n, number;//n为每次需要报的数,number为报数的总人数
int end[100], i = 0;//定义一个数组用来装每次的报数
scanf("%d", &number);//输出报数的总人数
first = Append(number); // 先构建一个数量为number的约瑟夫环
while (scanf("%d", &n) != EOF) {//先记录每次的报数情况,因为不清楚要报数多少次
end[i++] = n;
}
for (int j = 0; j < i; j++) {//根据前面记录的报数情况再次循环构建新的约瑟夫环
if (j == 0) {
second = Newjosephus(first, end[j]); // 构建新的约瑟夫环,第一次单独处理
} else {
second = Newjosephus(second, end[j]); // 再次构建约瑟夫环
}
}
Display(second); // 输出最终的约瑟夫环
return 0;
}
LinkList Append(int num) { // 构建单向循环链表,采用边构建边闭合的方式
LinkList pHead = NULL, pTail = NULL;//定义头指针和尾指针
for (int i = 1; i <= num; i++) {//循环报数的总人数次
LinkList newNode = (LinkList)malloc(sizeof(LinkNode)); // 先创建一个新结点
newNode->Data = i;
newNode->Next = NULL;
if (pHead == NULL) {//如果是第一个结点
pHead = pTail = newNode; // 让头指针和尾指针都指向该结点
pHead->Next = pHead; // 闭环
} else {//如果不是第一个结点
newNode->Next = pHead; // 先闭环
pTail->Next = newNode; // 尾插法
pTail = newNode;
}
}
return pHead; // 返回头指针
}
void Display(LinkList L) { // 输出该约瑟夫环
LinkList temp = L;
if (temp == NULL) return;//如果约瑟夫环为空直接返回
do {
printf("%4d", temp->Data);//先输出一次再判断是否这个循环链表已经输出完毕
temp = temp->Next;//继续输出下一个
} while (temp != L);
printf("\n");
}
LinkList Newjosephus(LinkList old, int times) { // 根据输出约瑟夫环改造,将输出环节和删除环节改为重新添加到一个单链表当中去
if (times == 1) { // 如果报数为1,直接输出链表,即让该链表的值原封不动的赋值给新的链表
LinkList temp = old;//让指针指向上一次构建的约瑟夫环的头
LinkList pHead = NULL, pTail = NULL;//定义头指针和尾指针
do {
LinkList newNode = (LinkList)malloc(sizeof(LinkNode));
newNode->Data = temp->Data;
newNode->Next = NULL;
if (pHead == NULL) {//尾插法构建单向循环链表
pHead = pTail = newNode;
pHead->Next = pHead;
} else {
newNode->Next = pHead;
pTail->Next = newNode;
pTail = newNode;
}
LinkList toDelete = temp;//依次删除报数为1的数
temp = temp->Next;
free(toDelete);
} while (temp != old);
return pHead;//返回头指针
}
int count = 0;
LinkList pHead = NULL, pTail = NULL;
do {
count++;
if (count == times - 1) {
LinkList pDel = old->Next;
old->Next = pDel->Next;
LinkList newNode = (LinkList)malloc(sizeof(LinkNode));
newNode->Data = pDel->Data;
newNode->Next = NULL;
if (pHead == NULL) {
pHead = pTail = newNode;
pHead->Next = pHead; // 闭环
} else {
newNode->Next = pHead; // 先闭环
pTail->Next = newNode;
pTail = newNode;
}
free(pDel); // 删除目标报数结点
count = 0; // 计数器重置为0
}
old = old->Next; // 指向下一个结点
} while (old->Next != old); // 当单链表只剩一个结点时退出循环
LinkList newNode = (LinkList)malloc(sizeof(LinkNode)); // 再将最后一个结点加入新链表当中去
newNode->Data = old->Data;
newNode->Next = pHead; // 先闭环
pTail->Next = newNode;
free(old);
return pHead; // 返回新链表的头结点
}