约瑟夫环问题(菜鸟自己的总结)

约瑟夫问题

首先约瑟夫问题: 据说著名犹太历史学家 Josephus有过以下的故事:在罗马人占领乔塔帕特后,39 个犹太人与Josephus及他的朋友躲到一个洞中,39个犹太人决定宁愿死也不要被敌人抓到,于是决定了一个自杀方式,41个人排成一个圆圈,由第1个人开始报数,每报数到第3人该人就必须自杀,然后再由下一个重新报数,直到所有人都自杀身亡为止。然而Josephus 和他的朋友并不想遵从。首先从一个人开始,越过k-2个人(因为第一个人已经被越过),并杀掉第k个人。接着,再越过k-1个人,并杀掉第k个人。这个过程沿着圆圈一直进行,直到最终只剩下一个人留下,这个人就可以继续活着。问题是,给定了和,一开始要站在什么地方才能避免被处决?Josephus要他的朋友先假装遵从,他将朋友与自己安排在第16个与第31个位置,于是逃过了这场死亡游戏。

现在解决的是一个类似的问题

17世纪的法国数学家加斯帕在《数目的游戏问题》中讲了这样一个故事:15个教徒和15 个非教徒在深海上遇险,必须将一半的人投入海中,其余的人才能幸免于难,于是想了一个办法:30个人围成一圆圈,从第一个人开始依次报数,每数到第九个人就将他扔入大海,如此循环进行直到仅余15个人为止。问怎样排法,才能使每次投入大海的都是非教徒。
**问题的思路

  1. 创建一个环链表 就可以不断的遍历
  2. 然后就得给每个节点赋值了 使用尾插法的话就得从30开始赋值了
  3. 还有要输出十五个数 那么就得有一个循环来十五次的输出
  4. 要寻找第九个人而且找到后第十个又从一开始报数
  5. 这个也可以通过for来寻找,然后空置节点 输出节点数据 释放空间
  6. 感觉这样一想是挺简单的,但是现在的我敲起来还是挺懵逼的
  • 首先搞个结构体
#include <stdio.h>
#include <stdlib.h>

struct node 
{
	int no;//这是个计数器
	struct node *link;
};
typedef struct node G;//相当于结构体的一个别名
  • 创建一个空环链表吧
   G *head;
   head = (G*)malloc(sizeof(G));
   head->no=-1;//搭建一个环空链表         
   head->link=head;//head->link指的是表头节点 

至于为什么要有表头节点,表头节点的有无区别在哪里
推荐一篇文章自己看看就懂了

  • 再得有个输入
for(i=30;i>0;i--)
   {
   	p = (G*)malloc(sizeof(G));
   	p->link=head->link;//表头节点的link是head所以p现在指向head 
   	p->no=i;
   	head->link=p;//再让head指向p 	
   }
  • 但是之后的找节点是用不着表头节点的,所以我们将它空置
    while(p->link!=head)
   		p=p->link;//找出最后一个节点 
     p->link=head->link;//让最后一个节点指向第一个数据节点,空出表头节点
  • 下一步就是问题解决的核心了
for(j=0;j<15;j++)//找十五个数 
   {
   	for(m=1;m<9;m++)//第九个删掉 
   		p=p->link;//找到第八个 
   	q=p->link;//找到第九个 
   	p->link=q->link;//让第八个指向第十个,空出第九个 
   	printf("%d\n",q->no);
   	free(q);//释放第九个的空间 
   }

这样就找出了一种排法,使每次投入大海的都是非教徒。

  • 下面是测试代码
#include <stdio.h>
#include <stdlib.h>

struct node 
{
	int no;
	struct node *link;
};
typedef struct node G;
int main()
{
	G *p,*q,*head;int i,j,m;
	head = (G*)malloc(sizeof(G));
	head->no=-1;//搭建一个环空链表         
	head->link=head;//head->link指的是表头节点 
	
	for(i=30;i>0;i--)
	{
		p = (G*)malloc(sizeof(G));
		p->link=head->link;//表头节点的link是head所以p现在指向head 
		p->no=i;
		head->link=p;//再让head指向p 
		
	}
	
	while(p->link!=head)
		p=p->link;//找出最后一个节点 
	p->link=head->link;//让最后一个节点指向第一个数据节点,空出表头节点
	printf("非教徒的排法:"); 
	for(j=0;j<15;j++)//找十五个数 
	{
		for(m=1;m<9;m++)//第九个删掉 
			p=p->link;//找到第八个 
		q=p->link;//找到第九个 
		p->link=q->link;//让第八个指向第十个,空出第九个 
		printf("%3d",q->no);
		free(q);//释放删掉的空间 
	}
	return 0;
}

2019/12/11 21:56

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值