这段时间要沉迷刷题一段时间了,就让优快云陪我一起吧!
一、题目大意
题目就是一个典型的约瑟夫环问题,就是给你一个整数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;
}
如果有问题,欢迎大家指正!!!