说到约瑟夫,你可能会想起到的是约瑟夫.摩根《吸血鬼日记》里的混血始祖克劳斯......但我们今天要说一说著名的约瑟夫问题。
约瑟夫问题(有时也称为约瑟夫斯置换,是一个出现在计算机科学和数学中的问题。在计算机编程的算法中,类似问题又称为约瑟夫环。又称“丢手绢问题”.)。
约瑟夫问题是个有名的问题:N个人围成一圈,从第一个开始报数,第M个将被杀掉,最后剩下一个,其余人都将被杀掉。
在此,有一个有趣的小故事:据说著名犹太历史学家 Josephus有过以下的故事:在罗马人占领乔塔帕特后,39 个犹太人与Josephus及他的朋友躲到一个洞中,39个犹太人决定宁愿死也不要被敌人抓到,于是决定了一个自杀方式,41个人排成一个圆圈,由第1个人开始报数,每报数到第3人该人就必须自杀,然后再由下一个重新报数,直到所有人都自杀身亡为止。然而Josephus 和他的朋友并不想遵从。首先从一个人开始,越过k-2个人(因为第一个人已经被越过),并杀掉第k个人。接着,再越过k-1个人,并杀掉第k个人。这个过程沿着圆圈一直进行,直到最终只剩下一个人留下,这个人就可以继续活着。问题是,给定了和,一开始要站在什么地方才能避免被处决?Josephus要他的朋友先假装遵从,他将朋友与自己安排在第16个与第31个位置,于是逃过了这场死亡游戏(学霸用事实证明知识就是力量!)。
简单的说就是i个人(编号1~i),从1开始报数,报到m的退出,剩下的人继续从1开始报数。求最后一个留下的人的编号。
我们知道第一个人(编号一定是m) 出列之后,剩下的i-1个人组成了一个新的
约瑟夫环
令f表示i个人玩游戏报m退出最后胜利者的编号,最后的结果自然是f[n]
递推公式
f[1]=0;
f=(f+m) mod i; (i>1)
下面用循环链表实现设个问题:
#include<stdio.h>
#include<stdlib.h>
typedef struct Node
{
int data;
struct Node* next;
}node_t;
#include<stdlib.h>
typedef struct Node
{
int data;
struct Node* next;
}node_t;
typedef struct Linklist
{
node_t *phead;
node_t *ptail;
int len;
}Linklist;
static node_t *GetNode(int i) //初始化节点
{
node_t*pNode=NULL;
pNode=(node_t*)malloc(sizeof(node_t));
if(!pNode)
{
printf("Error,out of memroy\n");
exit(-1);
}
pNode->data=i;
pNode->next=NULL;
return pNode;
}
void init_list(Linklist*plist) //用第一个节点初始化循环单链表
{
node_t*p;
p=GetNode(1);
plist->phead=p;
plist->ptail=p;
p->next=plist->phead;
plist->len=1;
}
static void Create_List(Linklist*plist,int n) //把数据添加到循环单链表中
{
int i=0;
node_t*pNew;
for(i=2;i<=n;i++)
{
pNew=GetNode(i);
plist->ptail->next=pNew;
plist->ptail=pNew;
pNew->next=plist->phead;
plist->len++;
}
printf("约瑟夫环的序列为:\n");
}
void Print_List(Linklist*plist) //输出链表内容
{
node_t*pCur=plist->phead;
do
{
printf("%d\n",pCur->data);
pCur=pCur->next;
}
while(pCur!=plist->phead);
printf("共有人数:%d\n",plist->len);
}
void joseph(Linklist*plist,int m) //约瑟夫回环实现
{
node_t*pPre=plist->ptail;
node_t*pCur=plist->phead;
int i;
while(plist->len!=1)
{
i=0;
while(i<m-1)
{
pPre=pPre->next;
i++;
}
pCur=pPre->next;
pPre->next=pCur->next;
free(pCur);
plist->len--;
}
printf("最后幸存者是:%d\n",pPre->data);
}
int main()
{
int n=0;
int m=0;
Linklist pList;
printf("输入约瑟夫环的人数:");
scanf("%d",&n);
printf("输入报的数:");
scanf("%d",&m);
init_list(&pList);
Create_List(&pList,n);
Print_List(&pList);
joseph(&pList,m);
system("pause");
return 0;
}
{
node_t *phead;
node_t *ptail;
int len;
}Linklist;
static node_t *GetNode(int i) //初始化节点
{
node_t*pNode=NULL;
pNode=(node_t*)malloc(sizeof(node_t));
if(!pNode)
{
printf("Error,out of memroy\n");
exit(-1);
}
pNode->data=i;
pNode->next=NULL;
return pNode;
}
void init_list(Linklist*plist) //用第一个节点初始化循环单链表
{
node_t*p;
p=GetNode(1);
plist->phead=p;
plist->ptail=p;
p->next=plist->phead;
plist->len=1;
}
static void Create_List(Linklist*plist,int n) //把数据添加到循环单链表中
{
int i=0;
node_t*pNew;
for(i=2;i<=n;i++)
{
pNew=GetNode(i);
plist->ptail->next=pNew;
plist->ptail=pNew;
pNew->next=plist->phead;
plist->len++;
}
printf("约瑟夫环的序列为:\n");
}
void Print_List(Linklist*plist) //输出链表内容
{
node_t*pCur=plist->phead;
do
{
printf("%d\n",pCur->data);
pCur=pCur->next;
}
while(pCur!=plist->phead);
printf("共有人数:%d\n",plist->len);
}
void joseph(Linklist*plist,int m) //约瑟夫回环实现
{
node_t*pPre=plist->ptail;
node_t*pCur=plist->phead;
int i;
while(plist->len!=1)
{
i=0;
while(i<m-1)
{
pPre=pPre->next;
i++;
}
pCur=pPre->next;
pPre->next=pCur->next;
free(pCur);
plist->len--;
}
printf("最后幸存者是:%d\n",pPre->data);
}
int main()
{
int n=0;
int m=0;
Linklist pList;
printf("输入约瑟夫环的人数:");
scanf("%d",&n);
printf("输入报的数:");
scanf("%d",&m);
init_list(&pList);
Create_List(&pList,n);
Print_List(&pList);
joseph(&pList,m);
system("pause");
return 0;
}
看了这篇文章,你是不是对约瑟夫问题理解了呢