问题描述
已知n个人(以编号1,2,3……,n分别表示)围坐在一张圆桌周围。从编号为k的人开始报数,数到m的那个人出列;他的下一个人又从k开始报数,数到m的那个人又出列;依此规律重复下去,直到圆桌周围的人全部出列。
本题就是约瑟夫斯环的实际场景。要通过输入n,k,m三个数,求出列的序列。比较方便的解法是通过一个无头结点的单项循环链表来完成。如下图所示,假定一开始有10个,从编号为3的人开始报数,数到5的人出列,即(n,k,m) = (10,3,5)。
代码实现
直接用一个函数Josephus()实现,函数参数为n、k、m,函数中主要做三件事。首先,根据参数n创建一个不含头结点的单项循环链表;然后,根据k确定最先开始报数的结点;最后,开始报数,删除结点,继续报数,继续删除结点的过程,直至所有结点全部删除。
Josephus.cpp
#include <stdio.h>
#include <stdlib.h>
#include <iostream>
#include <iomanip>
using namespace std;
typedef struct Node{
int data;
struct Node * next;
}node;
void Josephus(int n, int k, int m)
{
node * head;
node * tempNode,*p;
int i;
head = (node *)malloc(sizeof(node));
if(!head){
return ;
}
head->data = 1;
head->next = NULL;
p = head;
for(i=2;i<=n;i++){
tempNode = (node *)malloc(sizeof(node));
if(!tempNode){
return;
}
tempNode->data = i;
p->next = tempNode;
tempNode->next = head;
p = tempNode;
}
tempNode = head;
for(i=1;i<k;i++){
p = tempNode;
tempNode = p->next;
}
while(n>1){
for(i=k;i<m;i++){
p = tempNode;
tempNode = p->next;
}
cout << left << setw(5) << tempNode->data;
p->next = tempNode->next;
tempNode->next = NULL;
free(tempNode);
tempNode = p->next;
n --;
}
cout << left << setw(5) << tempNode->data << endl;
free(tempNode);
}
int main()
{
int n = 10;
int k = 3;
int m = 5;
Josephus(n,k,m);
cout << endl;
return 0;
}
运行结果
餐后甜品
约瑟夫斯问题:约瑟夫斯是耶路撒冷人,罗马皇帝提图斯的军事顾问、历史学家。据说在罗马人占领乔塔帕特斯后,39个犹太人与约瑟夫斯及他的朋友躲到一个山洞中,39个犹太人决定宁愿死也不要被人抓到,于是决定了一个自杀方式,41个人排成一个圆圈,由第1个人开始报数,每报数到第3人,该人就必须自杀,然后再由下一个人重新报数,直到所有人都自杀为止。然后约瑟夫斯和他的朋友并不像遵从,约瑟夫斯要他的朋友先假装遵从,他将朋友与自己分别安排在第16个与第31个为止,与是逃过了这场死亡游戏。
以上内容摘自百度百科·约瑟夫斯,我并没有仔细求证过,但是粗略验证了一下,在上述场景下,最后出列的人编号确实是16和31。
看小故事,纯粹是为了加深印象。不过,约瑟夫斯把自己安排在了真正的最后,这说明了什么呢?