第九届蓝桥杯(国赛)——约瑟夫环

题目描述
n 个人的编号是 1 ~ n,如果他们依编号按顺时针排成一个圆圈,从编号是1的人开始顺时针报数。

(报数是从 1 报起)当报到 k 的时候,这个人就退出游戏圈,下一个人重新从 1 开始报数。

求最后剩下的人的编号。这就是著名的约瑟夫环问题。

本题目就是已知 n,k 的情况下,求最后剩下的人的编号。

输入格式
题目的输入是一行,2 个空格分开的整数 n, k

输出格式
要求输出一个整数,表示最后剩下的人的编号。

样例输入
10 3

样例输出
4

数据范围
0 < n, k < 105


题解一
队列(会超时):

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

queue<int> q;

int main()
{
	int n, k;
	cin >> n >> k;
	
	for (int i = 1; i <= n; i ++) q.push(i);
	
	int num = 1;
	while(q.size()!= 1)
	{
		if(num == k)
		{
			q.pop();							
			num = 1;							// 重新报数 
		}
		else
		{
			q.push(q.front());					// 将队头放到队尾 
			q.pop();
			num ++;
		}
	}
	
	cout << q.front() << endl;
	return 0;
}

题解二
递推:

f[i]:有 i 个人参加游戏时,剩下的人的编号(下标从 0 开始)。

由于下标从 0 开始,所以报 k 的是第 k-1 个人。从下一个人开始,所有人重新从 0 开始编号,比如第 k 个人的编号变为 0,…

那么问题就转变成:有 i-1 个人参加游戏,剩下的人的编号,即 f[i - 1]

原编号与新编号相差 k,所以f[i] = f[i - 1] + k,又因为编号不能超过人数,所以还要 % i

#include <iostream>
using namespace std;

int f[1000010];

int main()
{
	int n, k;
	cin >> n >> k;
	
	for (int i = 1; i <= n; i ++) f[i] = (f[i - 1] + k) % i;
	
	cout << f[n] + 1 << endl;
	return 0;
} 
  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 这是一个关于的问题。(Joseph)是指编号为1、2、3……n的n个人按顺时针方向围坐在一圈。每个人持有一个密码(正整数)。一开始,任意指定一个正整数作为报数上限值m。从第一个人开始顺时针地逐个报数,到第m个人时停止报数。同时,这个人要将自己的密码告诉旁边的人。然后,从他的下一个人开始再次顺时针逐个报数,直到报到第m个人时再次停止报数,并告诉旁边的人自己的密码。如此循进行下去,直到最后只剩下一个人为止。这个问题要求选择一个正整数作为报数上限值m,使得在指定的n个人中,最后留下的那个人所持有的密码在第一个人开始报数时被报出来。 ### 回答2: 是一种经典的问题,其基本思路是利用循链表模拟人围成形的场景,从而通过一定规则不断删除元素,直到只剩下一个元素为止。这个问题涉及到了线性表中的循链表、链式存储结构、循操作等内容,具有很强的实际应用价值。 解决问题的基本思路是,从第一个人开始报数,每次报到m就出列,直到所有人都出列为止。为了能够便捷地删除被淘汰的人,我们需要采用链表的方式来实现。在具体实现时,我们可以将所有人看作链表中的结点,将它们通过指针相连成为一个循链表。由于循链表的最后一个结点指向链表的第一个结点,因此可以实现“形”的效果。 当然,在实际计算中还需要进行一些优化。比如,当当前报数的人数大于m时,可以利用循操作,方便地将当前结点移到距离它m位置后面的结点。此外,由于报数是形的,因此我们需要对序号进行取模操作。具体算法步骤如下: 1、将所有人构成一个循链表。 2、从第一个人开始报数(从1开始),每次报数都将当前结点移至距离它m的后面一个结点。 3、当当前人数小于或等于m时,删除当前节点,直至只剩下一个结点。 4、输出剩下的这个结点的编号,即为最后获胜者的编号。 总的来说,是一个非常有趣的问题,它不仅仅涉及到数据结构与算法方面的知识,还可以帮助我们锻炼逻辑思维和问题解决能力。同时,它也具有很多实际应用价值,比如密码学领域中的随机数生成、进程管理等方面。因此,我们应当及时学习与掌握这个问题的解决方法,以便更好地应对各种挑战。 ### 回答3: 一开始,让第一个人从1开始报数,依次下去,凡报到m的倍数的人都要出列,直到最后只剩下一个人为止。这个人就是问题的解。问题可以用线性表来解决。 线性表是指由n个数据元素$ {a_1,a_2,...,a_n} $ 组成的有限序列,这些元素可以是基本数据类型或者结构类型,它们之间的关系是线性的,即前驱后继关系唯一确定。 对于问题,我们可以用线性表来表示。可以用顺序表、链表或循链表来实现。其中,循链表最符合问题的特点,因为它可以将表首和表尾相连,形成一个。 具体实现方法是,将n个人依次插入循链表中,每个结点存储一个密码和一个指向下一个结点的指针。然后,从表头开始,每次数到m,就删除相应的结点,直到只剩下一个结点为止。最后留下的这个结点就是问题的解。 问题有很多种求解方法,除了线性表之外,还可以用递归、数学公式等方法来求解。但是,用线性表来解决问题具有简单易懂、效率高等优点,是最常用的方法之一。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值