【数据结构课程设计】题目二:约瑟夫生者死者游戏

本文深入探讨约瑟夫游戏的算法实现,包括模拟方法、递归解法及顺序和链式存储结构应用,旨在帮助读者理解并掌握这一经典问题的多种解决策略。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

约瑟夫游戏的大意:30 个游客同乘一条船,因为严重超载,加上风
浪大作,危险万分。因此船长告诉乘客,只有将全船一半的旅客投入
海中,其余人才能幸免于难。无奈,大家只得同意这种办法,并议定
30 个人围成一圈,由第一个人数起,依次报数,数到第 9 人,便把
他投入大海中,然后再从他的下一个人数起,数到第 9 人,再将他投
入大海中,如此循环地进行,直到剩下 15 个游客为止。问:哪些位
置是将被扔下大海的位置?

(1)不失一般性,将 30 改为一个任意输入的正整数 n,而报数上限
(原为 9)也为一个任选的正整数 k。

(2)要求使用顺序存储结构和链式存储结构分别实现。

吐槽:
很多题有很多方法,就一种方法又会有很多种表达形式,许多语法细节使用还可以不一样…

有时想把一些经典问题的做法好好归纳下,却又常常有种无从下手的感觉。

这里依次列了些方法,感觉整体的结构有些杂乱ㄟ( ▔, ▔ )ㄏ

推荐大家只看代码1就好了


1.直观想法:模拟

#include <iostream>
#include <cstring>
using namespace std;
int main()					//约瑟夫环问题"模拟"做法 
{
	int n,k,i=0;			//总人数 报数上限 当前角色对应编号 
	cin>>n>>k;
	int a[n],sum=n,num=0;	//模拟数组  圈内人数  计数器 
	memset(a,0,sizeof(a));	//0表示在环中 
	while(sum>15)			//总人数大于15游戏继续,这里15可以根据情况不同调节 
	{
		i=i%n;				//i是从0开始的,i到了n就归零 
		if(!a[i]) num++;	//当前角色在圈中,则计数++
		if(num==k)			//当前角色是第k人,出圈四步骤如下: 
		{
			cout<<i+1<<" "; //1.输出位置号码 ,这里i是在数组里的编号,故输出要记得+1 
			a[i]=1;			//2.将其标记成已出圈 
			num=0; 			//3.计数器num标记成0
			sum--;			//4.总人数减一 
		}
		i++; 
	}	 
	return 0;
}
//9 18 27 6 16 26 7 19 30 12 24 8 22 5 23

2.顺序表:(模拟解法)

题目要求顺序存储结构,那我这里就想办法改编的像当时学数据结构时的顺序表好了

#include <bits/stdc++.h>
using namespace std;
const int maxn=1e5+10;
struct node{
	int *a=NULL;
	int sum;//圈内人数 
};
void init(node &L,int n)
{
	//L.a=new int[n];
	//L.a=(int*)malloc(n*sizeof(int)); 
	L.a=(int*)calloc(n,sizeof(int)); 
	//cout<<sizeof(L.a)<<endl;
	//memset(L.a,0,n*sizeof(L.a));//0表示在环中 
	L.sum=n;
}
void destroy(node &L)
{
	delete L.a;
}

void ysf(node &List,int n,int k)
{
	int i=0,num=0;  //当前角色对应编号 计数器 
	while(List.sum>15)//总人数大于15游戏继续,这里15可以根据情况不同调节 
	{
		i=i%n;//i是从0开始的,i到了n就归零 
		if(!List.a[i]) num++;//当前角色在圈中,则计数++
		if(num==k)//当前角色是第k人,出圈 
		{
			cout<<i+1<<" ";//输出位置号码 ,这里i是在数组里的编号,故输出要记得+1 
			List.a[i]=1;// 将其标记成已出圈 
			num=0;//计数器num标记成0
			List.sum--;//总人数减一 
		}
		i++; 
	}	 
	destroy(List);
}
int main()
{
	int n,k;//总人数 报数上限 
	cin>>n>>k;
	node List;
	init(List,n);
	ysf(List,n,k);
	return 0;
}


3.顺序表:(有带删除操作)

感觉代码2还是没有当时学顺序表的感觉,这里就又换了个形式

#include <bits/stdc++.h>
using namespace std;
const int maxn=1e5+10;

struct node{
	int *a=NULL;
	int length;//报数序列  即计数器 
	int size; //存储总大小
	int sum ; //当前存活人数 
}L;

void init(int n,int k)
{
	L.a=new int[n];
	L.length=0;
	L.size=n;
	L.sum=n;
	for(int i=0;i<L.size;i++)
	{
		L.a[i]=i+1;
	}
}

void delete_List(int k)
{
	for(int i=k;i<L.sum;i++)
	{
		L.a[i-1]=L.a[i];
	}
}
void ysf(int n,int k)
{
	while(L.sum>15)//这里可根据实际情况调节、
	{
		L.length=L.length+k;
		if(L.length>L.sum) 
		L.length%=L.sum;
		if(L.length==0) L.length=L.sum;
		cout<<L.a[L.length-1]<<" ";
		delete_List(L.length);
		L.length--;
		L.sum--;
	} 
}

int main()
{
	int n,k;//总人数  报数上限
	cin>>n>>k;
	init(n,k); 
	ysf(n,k);
	return 0;
}


4.带头循环链表:

题目要求链式存储结构,实现形式可以多种多样,这里例举一种符合链表思维的一种形式:

#include <bits/stdc++.h>
using namespace std;

//本题采用循环链表解决 约瑟夫环问题 
typedef struct node{
	int	alive;
	node *next;
}*pnode; 

void init(pnode &head)
{
	head = new node;
	head->alive=-1;//头结点不参与计数 
	head->next=NULL;
}

void build(pnode head,int n)
{
	pnode p=head;
	for(int i=1;i<=n;i++)//为使链表依次从小到大,采用尾插法 
	{
		pnode a=new node;
		a->alive=i;
		head->next=a;
		a->next=NULL;
		if(n==i) a->next=p->next;//这里采用的是带头结点的循环链表表示形式 
		head=a;
	} 	
	//head=p;  这里形参head若是指针的引用 \
	这个函数中关于head的操作会影响到其实际的值 ,这样就要加上前面那条语句 
}

void delete_node(pnode pre,pnode p)
{
	pre->next=p->next;
	p->next=NULL;
	delete p;
}

pnode find(pnode p,int k)
{
	pnode pre=NULL;
	while(k--)
	{
		pre=p;
		p=p->next;
	}
	if(p)
	{
		cout<<p->alive<<" ";
		delete_node(pre,p);
	}
	return pre;
}


void ysf(pnode head,int sum,int k)
{
	pnode p=head;
	while(sum>15)//这里15可根据实际情况调节
	{
		p=find(p,k);
		sum--;
	} 
} 
int main()
{
	pnode head=NULL;//node* head
	init(head);
	int n,k;
	cin>>n>>k;
	cout<<head<<endl;
	build(head,n);  
	cout<<head<<endl;
	ysf(head,n,k);//头指针  开始总人数  报数上限 
}

5.递归解法

最后分享再分享一个递归解法
代码来源于:https://blog.youkuaiyun.com/yanweibujian/article/details/50876631

#include <bits/stdc++.h>
using namespace std;
const int maxn=1e5+10;
int ysfdg(int sum,int value,int n)
{
    if(n==1)
        return (sum+value-1)%sum;
    else
        return (ysfdg(sum-1,value,n-1)+value)%sum;
}

int Joseph(int sum,int value,int n)//剩余人总数  报数上限  扔下海的第n个人 
{
	if(n==1)
		return value%sum;//这么写会有值为零 
	else
		return (Joseph(sum-1,value,n-1)+value)
}
int main()
{
	int sum=30,alive=15,i,count=9;
	 for(i=1;i<=sum-alive;i++)
        printf("第%2d个被扔海里人的编号:%2d\n",i,ysfdg(sum,count,i)+1);
	return 0;
}


先分享这点吧,方法肯定不止这些,但我觉得其实掌握模拟方法,理解递归方法就足够了
ㄟ( ▔, ▔ )ㄏ

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值