关于约瑟夫问题的那点事

  说到约瑟夫,你可能会想起到的是约瑟夫.摩根《吸血鬼日记》里的混血始祖克劳斯......但我们今天要说一说著名的约瑟夫问题

 约瑟夫问题(有时也称为约瑟夫斯置换,是一个出现在计算机科学和数学中的问题。在计算机编程的算法中,类似问题又称为约瑟夫环。又称“丢手绢问题”.)。

 约瑟夫问题是个有名的问题: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;

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;
}



  看了这篇文章,你是不是对约瑟夫问题理解了呢 大笑



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值