约瑟夫环问题——4个解法总结(C语言)

约瑟夫问题:有n只猴子,按顺时针方向围成一圈选大王(编号从1到n),从第1号开始报数,一直数到m,数到m的猴子退出圈外,剩下的猴子再接着从1开始报数。就这样,直到圈内只剩下一只猴子时,这个猴子就是猴王,编程求输入n,m后,输出最后猴王的编号。

样例输入

6 2

样例输出

5

样例输入

12 4

样例输出

1

1.循环链表实现

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>

typedef struct link
{
	int data;
	link* next;
}link;

int main()
{
	int n, m;
	int i;
	printf("请输入(n/m):");
	scanf("%d%d", &n, &m);

	link* head = (link*)malloc(sizeof(link));//头节点
	head->data = -1;
	head->next = NULL;

	link* tail, * p, * q;

	tail = head;//构造循环节点
	for (i = 0; i < n; i++)
	{
		p = (link*)malloc(sizeof(link));
		p->data = i + 1;
		tail->next = p;
		p->next = head->next;
		tail = p;
	}

	p = head->next;//p为要删除的那一个指针,q为p的前一个位置的指针
	q = tail;
	i = 1;//用i来报数
	while (p != q)//直到两个指针相同时,循环链表内除开头节点外只有一个节点了
	{
		if (i == m)//当报数为m时,删除p,同时让i=1
		{
			i = 1;
			q->next = p->next;
			free(p);
			p = q->next;
		}
		else
		{
			q = p;
			p = p->next;
			i++;
		}
	}
	printf("最后留下的猴子序号为: %d", p->data);
	free(p);
	free(head);
	return 0;
}

2.数组标志位实现

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>

int main()
{
	int n, m;
	int i;
	int monkey[300] = { 0 };

	printf("请输入(n/m): ");
	scanf("%d%d", &n, &m);
	for (i = 0; i < n; i++)
	{
		monkey[i] = i + 1;
	}

	int number = n;//记录此时数组中有效元素的个数
	int count = 1;//用来报数
	int pos = 0;//记录数组下标位置
	while (number > 1)
	{
		if (monkey[pos] > 0)
		{
			if (count == m)
			{
				monkey[pos] = 0;
				count = 1;
				number--;
				pos = (pos + 1) % n;
			}
			else
			{
				count++;
				pos = (pos + 1) % n;
			}
		}
		else
			pos = (pos + 1) % n;
	}

	for (i = 0; i < n; i++)
		if (monkey[i] > 0)
			printf("最后留下的猴子序号为:%d\n", monkey[i]);

	return 0;
}

3.数组模拟(循环)链表实现

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>

int main()
{
	int n, m;
	int monkey[300] = { 0 };
	int i;

	printf("请输入(n/m): ");
	scanf("%d%d", &n, &m);
	for (i = 0; i < n - 1; i++)//数组模拟循环链表,数组的值相当于链表的next,指向下一个数组结点的下标
	{
		monkey[i] = i + 1;
	}
	monkey[i] = 0;

	int number = n;//记录当前数组有效元素个数
	int count = 1;//用来报数
	int pos = 0;
	int prior = n - 1;

	while (number > 1)//或者while(prior!=pos)
	{
		if (count != m)
		{
			count++;
			prior = pos;
			pos = monkey[pos];
		}
		else
		{
			monkey[prior] = monkey[pos];
			monkey[pos] = -1;
			pos = monkey[prior];
			count = 1;
			number--;
		}
	}
	printf("最后留下的猴子序号为:%d\n", pos + 1);
	return 0;
}

4.数学方法(原理自己百度查)

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>

int main()
{
	int n, m;
	printf("请输入(n/m):");
	scanf("%d%d", &n, &m);
	int pos = 0;
	for (int i = 2; i < n + 1; i++)
	{
		pos = (pos + m) % i;
	}
	printf("最后留下的猴子序号为:%d\n", pos + 1);
	return 0;
}

背景   约瑟夫问题(Josephus Problem)据说著名犹太历史学家 Josephus有过以下的故事:在罗马人占领乔塔帕特后,39 个犹太人与Josephus及他的朋友躲到一个洞中,39个犹太人决定宁愿死也不要被敌人到,于是决定了一个自杀方式,41个人排成一个圆圈,由第1个人开始报数,每报数到第3人该人就必须自杀,然后再由下一个重新报数,直到所有人都自杀身亡为止。 然而Josephus 和他的朋友并不想遵从,Josephus要他的朋友先假装遵从,他将朋友与自己安排在第16个与第31个位置,于是逃过了这场死亡游戏。   原题:  用户输入M,N值,N个人围成一个环,从0号人开始数,数到M,那个人就退出游戏,直到最后一个人 求最后一个剩下的人是几号? 问题描述   设编号为1-n的n(n>0)个人按顺时针方向围成一圈.首先第1个人从1开始顺时针报数.报m的人(m 为正整数).令其出列。然后再从他的下一个人开始,重新从1顺时针报数,报m的人,再令其出列。如此下去,直到圈中所有人出列为止。求出列编号序列。 基本要求   需要基于线性表的基本操作来实现约瑟夫问题   需要利用数组来实现线性表      输入输出格式   输入格式:n,m   输出格式1:在字符界面上输出这n个数的输出序列    输出格式2:将这n个数的输出序列写入到文件中 选做内容 使用单链表来实现之 使用循环链表来实现之 测试用例   输入:10,3   输出:3 6 9 2 7 1 8 5 10 4
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值