1.1.Josephus_Problem 约瑟夫问题

题目描述

约瑟夫问题 (本题要求用循环链表实现)

约瑟夫问题是一个经典的问题。已知n个人(不妨分别以编号1,2,3,…,n 代表 )围坐在一张圆桌周围,从编号为 k 的人开始,从1开始顺时针报数1, 2, 3, ...,顺时针数到m 的那个人,出列并输出。然后从出列的下一个人开始,从1开始继续顺时针报数,数到m的那个人,出列并输出,…依此重复下去,直到圆桌周围的人全部出列。

输入:n, k, m

输出:按照出列的顺序依次输出出列人的编号,编号中间相隔一个空格,每10个编号为一行

非法输入的对应输出如下

a)

输入::nkm任一个小于1
输出:n,m,k must bigger than 0.

b)

输入:k>n

输出:k should not bigger than n.

例:

输入:9,3,2

输出:4 6 8 1 3 7 2 9 5

解题思路

这个题就是一个简单的模拟,大佬还有更简单的数组方法,已经在结尾部分大致说明了。

(然而我并不懂这个)还是最好按照题目要求使用循环链表(

由于我最初理解错了,我写出了双向循环链表:(which下一个题需要两个方向都数,就可以用)

2.3.Josephus_problem_bidirectional 双向约瑟夫问题_ttttttvc的博客-CSDN博客

struct p{
	int id;
	struct p *prior,*next;
};
​struct p first,*cur,*ppre=&first,*pnex=(struct p*)malloc(sizeof(struct p));
	first.id=1;
	first.next=pnex;
    if(k==1) cur=&first;
	for(int i=1;i<n-1;i++)
	{
		pnex->id=i+1;
		if(i+1==k)
		{
			cur=pnex;
		}
		pnex->prior=ppre;
		pnex->next=(struct p*)malloc(sizeof(struct p));
		ppre=pnex;
		pnex=pnex->next;
	}
	pnex->id=n;
	pnex->prior=ppre;
	pnex->next=&first;
    if(k==n) cur=pnex;
	first.prior=pnex;

​(这里ppre和pnex指向前后两个在使用的结构,并用cur来记录了第k个人的位置)

所以实际上只需要这样就行了:

​struct p{
	int id;
	struct p *next;
};
​struct p first,*cur,*pnex=(struct p*)malloc(sizeof(struct p));
	first.id=1;
	first.next=pnex;
    if(k==1) cur=&first;
	for(int i=1;i<n-1;i++)
	{
		pnex->id=i+1;
		if(i+1==k)
		{
			cur=pnex;
		}
		pnex->next=(struct p*)malloc(sizeof(struct p));
		pnex=pnex->next;
	}
	pnex->id=n;
	pnex->next=&first;
    if(k==n) cur=pnex;

接下来是一个过程模拟,应该非常简单

注意事项是:这里只需要走m-1步到达第m个人;具体实现上只能走m-2步,这样有利于删除该元素。另外一种想法是不用删除元素,直接使用一个结构体标记,每次到达已经出圈的人之后就跳过,但我觉得可能会增加运行时间

int cnt = 0;
	while (cur->next != cur)
	{
		for (int j = 1; j < m - 1; j++)
		{
			cur = cur->next;
		}
		if (cnt == 9)
		{
			printf("%d\n", cur->next->id);
			cnt = 0;
		}
		else
		{
			printf("%d ", cur->next->id);
			cnt++;
		}
		cur->next = cur->next->next;
		cur = cur->next;
	}
	printf("%d\n", cur->id);

然后还有一点是这个cnt,为了解决题目要求很烦人的一个点:10个一排,换行。然后最后一个元素总是要换行。

贴源代码

(实际上就是C,我的三脚猫功夫…)

#include<iostream>
#include<cstdlib> 
using namespace std;

struct p {
	int id;
	struct p * next;
};

int main()
{
	int n, k, m;
	scanf_s("%d%*c%d%*c%d", &n, &k, &m);
	if ((n < 1) || (k < 1) || (m < 1))
	{
		cout << "n,m,k must bigger than 0.\n";
		return 0;
	}
	if (k > n)
	{
		cout << "k should not bigger than n.\n";
		return 0;
	}
    //两种特例

	struct p first, * cur=NULL, * pnex = (struct p*)malloc(sizeof(struct p));
	first.id = 1;
	first.next = pnex;
	if (k == 1) cur = &first;
	for (int i = 1; i < n - 1; i++)
	{
		pnex->id = i + 1;
		if (i + 1 == k)
		{
			cur = pnex;
		}
		pnex->next = (struct p*)malloc(sizeof(struct p));
		pnex = pnex->next;
	}
	pnex->id = n;
	pnex->next = &first;
	if (k == n) cur = pnex;
	/*cur=&first;
	for(int w=0;w<n+1;w++)
	{
		printf("%d\n",cur->id);
		cur=cur->next;
	}用于链表构成之后检验*/

	int cnt = 0;
	while (cur->next != cur)
	{
		for (int j = 1; j < m - 1; j++)
		{
			cur = cur->next;
		}
		if (cnt == 9)//应该换行了
		{
			printf("%d\n", cur->next->id);
			cnt = 0;
		}
		else
		{
			printf("%d ", cur->next->id);
			cnt++;
		}
		cur->next = cur->next->next;
		cur = cur->next;
	}
	printf("%d\n", cur->id);

	return 0;
}


继续学习

下面拓展一下约瑟夫出圈的黑暗故事……

据说着名犹太历史学家 Josephus有过以下的故事。

在罗马人占领乔塔帕特后,39 个犹太人与Josephus及他的朋友躲到一个洞中,39个犹太人决定宁愿死也不要被敌人找到,于是决定了一个自杀方式:41个人排成一个圆圈,由第1个人 开始报数,每报数到第3人该人就必须自杀,然后再由下一个重新报数,直到所有人都自杀身亡为止。 然而Josephus 和他的朋友并不想遵从,Josephus要他的朋友先假装遵从,他将朋友与自己安排在第16个与第31个位置,于是逃过了这场死亡游戏。

这里用例就是41,1,3…是不是突然觉得有些难过了…为纪念这个故事我把下面的测试结果放出来:

具有特殊意义的用例:41,1,3

然后我们讨论对于单向的Josephus_problem的最简单方法:数组法

其实一个最简单且不用list实现的方式就是利用(下标+1)%n,当然也是经常能接触到的东西(但我怎么就不会用),所以其实可以先利用循环,用count来记录出圈的是第几个人,然后一个int position来记录到达的位置

应该非常容易实现,所以我决定马上手撕(锻炼一下我可怜的编程能力啦)

​    int used[n] = {0};
    int position = k;
	for (int count = 0; count < n; count++) {
		for (int i = 0; i < m - 1; i++) {
			position = (position + 1) % n;
			while(used[position]) {
				position = (position + 1) % n;//当前已出圈时,移动到下一个还未出圈的
			}
		}//m次报数,m-1次移动

		if (0 == position)
			printf("%d,", n);
		else
			printf("%d,", position);
		used[position] = 1;

		do {
			position = (position + 1) % n;
		} while (used[position]);
		//首先到下一个,如果下一个已经出圈,移动到下一个还未出圈的
	}

​

——2022.9.22

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值