【约瑟夫环】POJ 1012 Joseph

博客围绕POJ 1012约瑟夫环问题展开。题目要求给定整数k,2k个人围坐一圈,前k个是好人,后k个是坏人,报数到m的人被处死,求最小的m使坏人全死后好人不死。作者先暴力模拟超时,后介绍打表法和利用递推公式求解,还说明了递推公式编号方式及原理,并给出AC代码。

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

这段时间要沉迷刷题一段时间了,就让优快云陪我一起吧!

一、题目大意

题目就是一个典型的约瑟夫环问题,就是给你一个整数k(1<k<14),有2k个人围坐一圈,前k个人是好人,后k个人是坏人,要求这2k个人从1开始依次报数,每报到m,则该人被处死,然后从他后面那个人开始报1,再继续报数,要求一个最小的m,使得在坏人死完之前,没有好人死去。

二、题目思路以及AC代码

一开始以为就是一道模拟(因为以前并没有接触过约瑟夫问题),然后就按照直接暴力模拟去做的,不出意外的TLE了。

然后我就去寻找题解,找到了 POJ 1012 。这里面给出的方法很简单,但是却有一点问题,我接下来也会提到。

对于这题的思路,直接暴力模拟超时了,我们就要考虑别的办法,当然,因为k并不大,可以自己在电脑上计算出答案后,直接打表,这是一种办法。

还有一个方法就是利用约瑟夫问题的递推公式:
在这里插入图片描述
但是,这个递推公式想要搞明白,还要先明白它是如何进行编号的,这也是我说之前的题解有问题的地方,这个递推公式的编号方式我就直接举个例子来说明吧,应该会更清楚一些。

比如我们给出k = 3,m = 5。
那么一开始编号为 0, 1, 2, 3, 4, 5,然后利用递推公式求得ans[1]为4,则杀掉4,那么这时,没有新编号之前剩下的人是0, 1, 2, 3, 5,然后对这些人重新编号为0, 1, 2, 3 ,4,再次根据递推公式求得ans[2]为3,则杀掉3,剩下0, 1, 2, 5,重新编号为0, 1, 2, 3,再次根据公式求得ans[3]为3,则杀掉3,也就是原来编号为5的人。

这样就可以实现约瑟夫环了,但为什么编号变化了,也可以解决该问题呢?因为我们的要求是在杀掉原编号小于k的人之前杀掉所有的原编号大于等于k的人,既然这样,按照这种编号方式,前k个人只要不被杀掉,其序号是不会变的,所以同样,只要判断杀掉的编号不小于k就好了。

下面给出AC代码:

#include<iostream>
using namespace std;

int main(void)
{
	int Joseph[14] = { 0 };  //打表,保存各个k值对应的m值

	int k;
	while (cin >> k)
	{
		if (!k)
			break;

		if (Joseph[k])
		{
			cout << Joseph[k] << endl;
			continue;	
		}

		int n = 2 * k;  //总人数
		int ans[30] = { 0 };  //第i轮杀掉 对应当前轮的编号为ans[i]的人
							  //PS:每一轮都以报数为“1”的人开始重新编号

		int m = k + 1;    //所求的最少的报数
		for (int i = 1; i <= k; i++)  //轮数
		{
			ans[i] = (ans[i - 1] + m - 1) % (n - i + 1);   //n-i为剩余的人数
			if (ans[i]<k)  //把好人杀掉了,m值不是所求
			{
				i = 0;
				m++;  //枚举m值
			}
		}
		Joseph[k] = m;
		cout << m << endl;
	}
	return 0;
}

如果有问题,欢迎大家指正!!!

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值