数据结构实验四 约瑟夫生死游戏

实验四 约瑟夫生死游戏

1、实验目的:
利用线性表解决实际问题。

2、实验环境与设备:
已安装Visual Studio 2010(或其以上版本)集成开发环境的计算机。

3、实验原理:
(1)利用线性表的删除功能剔除被杀掉的人。
(2)利用单链表、不带头结点的循环链表或带头结点的循环链表均可实现。

4、实验内容:
约瑟夫生死游戏问题有如下几种表述;
表述一:古代某法官要判决N个犯人的死刑,他有一条荒唐的法律,将犯人站成一个圆圈,从第S个人开始数起,每数到第D个犯人,就拉出来处决,然后再数D个,数到的再处决……直到剩下的最后一个可赦免。
表述二:17世纪的法国数学家加斯帕在《数目的游戏问题》中讲了这样一个故事: 15个教徒和15 个非教徒在深海上遇险,必须将一半的人投入海中,其余的人才能幸免于难,于是想了一个办法: 30个人围成一圆圈,从第一个人开始依次报数,每数到第九个人就将他扔入大海,如此循环进行直到仅余15个人为止。问怎样排法,才能使每次投入大海的都是非教徒。
表述三:30个旅客同乘一条船,因为严重超载,加上风高浪大,危险万分;因此船长告诉乘客,只有将全船一半的旅客投入海中,其余人才能幸免遇难。无奈,大家只得同意这种办法,并议定30个人围成一圈,由第一个人开始,依次报数,数到第9人,便把他投入大海中,然后从他的下一个人数起,数到第9人,再将他投入大海,如此循环,直到剩下15个乘客为止。问哪些位置是将被扔下大海的位置。
表述四:据说著名犹太历史学家 Josephus有过以下的故事:在罗马人占领乔塔帕特后,39 个犹太人与Josephus及他的朋友躲到一个洞中,39个犹太人决定宁愿死也不要被敌人抓到,于是决定了一个自杀方式,41个人排成一个圆圈,由第1个人开始报数,每报数到第3人该人就必须自杀,然后再由下一个重新报数,直到所有人都自杀身亡为止。然而Josephus 和他的朋友并不想遵从。首先从一个人开始,越过k-2个人(因为第一个人已经被越过),并杀掉第k个人。接着,再越过k-1个人,并杀掉第k个人。这个过程沿着圆圈一直进行,直到最终只剩下一个人留下,这个人就可以继续活着。问题是,给定了规则,一开始要站在什么地方才能避免被处决?Josephus要他的朋友先假装遵从,他将朋友与自己安排在第16个与第31个位置,于是逃过了这场死亡游戏。
其数学模型归结如下:
数学模型:假设n个人排成一个环形,依次顺序编号1,2,…,n。从第1号开始,沿环计数,每数到第m个人就让其出列,且从下一个人开始重新计数,继续进行下去。这个过程一直进行到剩下k个人为止。
试利用线性表编程模拟该游戏。
5、实验考核:
(1)完成纸质版实验报告
(2)提交电子版作业

6、执行结果示例如下:

图1 示例一
在这里插入图片描述
图2 实例二
在这里插入图片描述
图3 示例三
在这里插入图片描述
上代码

#include<stdio.h>
#include<malloc.h>
#define OK 1
#define FALSE -1
#define TURE 1
#define ERROR -1
#define INFEASIBLE -1

typedef int Elemtype;
typedef struct LNode
{
	Elemtype data;
	struct LNode *next;
}LNode, *LinkList;

LinkList p, r, last = NULL;
// 构造一个不带头结点的循环链表,并赋值 1~n 
void CreatList(int n)
{
	for (int i = 1; i <= n; i++)
	{
		p = (LinkList)malloc(sizeof(LNode)); // 申请一个新的链结点
		p->data = i;  // 存放第 i 个结点的元素  
		if (last == NULL)
			last = p;
		else
			r->next = p;
		r = p;
	}
	p->next = last;
	p = last; // 循环链表建立完成  
}
// 将头指针 p 指向第 s 个人 ,从第几个人开始 
void StartNum(int s)
{
	for (int i = 1; i < s; i++)
	{
		r = p;
		p = p->next;
	}// 此时 p 指向第 1 个出发结点
}
// 依次输出被删除的元素及最后剩余的元素。
void DeleteList(int m)
{
	while (p->next != p)//不指向自己
	{
		for (int i = 1; i < m; i++)
		{
			r = p;
			p = p->next;
		}//p 指向第 m 个结点 ,r 指向第 m-1 个结点
		r->next = p->next;// 删除第 m 个结点
		printf("Number %d is out\n", p->data);// 依次输出删除结点的元素  
		free(p);// 释放被删除结点的空间
		p = r->next;//p 指向新的出发结点
	}
	printf("Number %d is save\n", p->data);// 输出最后一个结点的元素  
}

void DeleteNum(int m,int save,int n)
{
	while (p->next!=p)//不指向自己
	{
		for (int i = 1; i < m; i++)
		{
			r = p;
			p = p->next;
		}//p 指向第 m 个结点 ,r 指向第 m-1 个结点
		r->next = p->next;// 删除第 m 个结点
		printf("Number %d is out\n", p->data);// 依次输出删除结点的元素  
		free(p);// 释放被删除结点的空间
		p = r->next;//p 指向新的出发结点
		n--;
		if (n == save) break;
	}

	while (p->next != NULL) {
		printf("Number %d is save\n", p->data);
		p = p->next;
		save--;
		if (save == 0) break;
	}
}


//大佬算法,一行解决!!
int f(int n, int m)//n:人数   m:间隔
{
	return n == 1 ? n : (f(n - 1, m) + m - 1) % n + 1;
}

int main()
{
	int n, m, s,save = 1;//n总人数    m间隔   s开始的位置  先默认save=1
	printf("请输入总人数 n:\n");
	//总人数
	scanf_s("%d", &n);
	//间隔
	printf("请输入间隔 m:\n");
	scanf_s("%d", &m);
	//开始的位置
	printf("请输入开始的位置 s:\n");
	scanf_s("%d", &s);
	//要剩的人数
	printf("请输入要保留的人数 s:\n");
	scanf_s("%d", &save);
	
	CreatList(n);
	StartNum(s);
	
	printf("****** Solve Josephus Problem ******\n");
	DeleteNum(m,save,n);
	printf("**************** END ***************\n");
	

	//还有个大佬算法,只保留一个人,递归调用,一行出结果
	// printf("%d\n",f(n,m));

	system("pause");
	return 0;
}

结果
(1)演示6剩1(间隔3)
在这里插入图片描述
(2)表述二和三:30剩15(间隔9)
在这里插入图片描述
(3)表述四:41剩2(间隔3)
在这里插入图片描述

发布了15 篇原创文章 · 获赞 0 · 访问量 241

猜你喜欢

转载自blog.csdn.net/qq_44796882/article/details/105464649