约瑟夫问题的描述:
据说著名犹太历史学家 Josephus有过以下的故事:在罗马人占领乔塔帕特后,39 个犹太人与Josephus及他的朋友躲到一个洞中,39个犹太人决定宁愿死也不要被敌人抓到,于是决定了一个自杀方式,41个人排成一个圆圈,由第1个人开始报数,每报数到第3人该人就必须自杀,然后再由下一个重新报数,直到所有人都自杀身亡为止。然而Josephus 和他的朋友并不想遵从。首先从一个人开始,越过k-2个人(因为第一个人已经被越过),并杀掉第k个人。接着,再越过k-1个人,并杀掉第k个人。这个过程沿着圆圈一直进行,直到最终只剩下一个人留下,这个人就可以继续活着。问题是,给定了和,一开始要站在什么地方才能避免被处决?Josephus要他的朋友先假装遵从,他将朋友与自己安排在第16个与第31个位置,于是逃过了这场死亡游戏。
分析问题:
本程序利用单向循环链表模拟每一个人,通过不断循环不断淘汰(即删除结点),直至还剩下最后一个人(即只有最后一个结点,head->next = head)
代码:
//Filename:JosephProblem.cpp
//Writed by CaoLichen
//本程序利用循环链表解决约瑟夫问题
#include<stdio.h>
#include<stdlib.h>
#define TRUE 1
//定义结点类型
typedef struct Node
{
int pos; //每个人位置标号
struct Node *next; //指向后继结点
}CLink;
/*建立循环链表,共有sum个结点,
头结点(位置号为1)已经确定,
所以还需插入(sum-1)个结点*/
void CreateLink(CLink *head,int sum)
{
CLink *tmp;
for(; sum > 1; sum --){
tmp = (CLink *)malloc(sizeof(CLink)); //插入结点
tmp->pos = sum;
tmp->next = head->next;
head->next = tmp;
}
}
//模拟出局过程
void OutLink(CLink *head, int skip)
{
printf("出局顺序为:\n");
int count = 1; //计数器,初始为1,即head结点
CLink *tmp;
while(TRUE){
head = head->next;
count ++;
if(head->next == head){ //循环链表只剩下一个结点,即只有最后一个人
printf("\n最后一个出局的是:%d\n",head->pos);
break;
}
if(count == skip-1){ //定位在出局结点的前继结点,双向循环链表可以直接定位在出局的结点
printf("%d ",head->next->pos);
tmp = head->next; //删除出局结点
head->next = tmp->next;
head = head->next;
free(tmp); //释放空间
count = 1; //重置计数器
}
}
}
int main()
{
CLink *head; //头结点
head = NULL;
head = (CLink *)malloc(sizeof(CLink)); //申请空间
head->next = head; //循环链表,初始化时头结点指向自己
head->pos = 1; //头结点定位为1
int sum; //总人数
printf("请输入总人数:");
scanf("%d",&sum);
int skip; //
printf("请输入报到第几个数就出局:");
scanf("%d",&skip);
CreateLink(head,sum); //创建循环链表并初始化每个位置编号
OutLink(head,skip); //模拟出局过程
return 0;
}