_考查点:
约瑟夫问题的变形。
_思路:
这里是要知道每一次出局的人的范围。而最初的约瑟夫问题只能知道最后一个出局的人的编号。
我所知道的有两种方法(均是数学方法)一个是暴力枚举加打表(0MS过掉),一个不用打表(论证了m满足一定条件不用暴力枚举)。
(第二个方法未理解,以后加上)。我们先说第一个方法,我们知道前k次killing,killed_man满足是(编号0~ 2 * k - 1)最后的k个人。
这样的m就符合要求。那么我们不去关心kill掉一个人后,他后面的人原来的编号(这个题不关心编号的问题)。
只需要满足每一次(一共k次)killing是在第k个人(编号k - 1)之后就行。那么每杀掉一个,
就将被杀的那个人(设编号为killed)后面的人的编号依次变为killed,killed + 1,...,2 * k - 1 - i(i为被杀次数).
虽然总人数在减小,我们只需要满足前k次killing的是在编号k - 1之后就行了。
那么就有关系式f [ i ] = ( f[i - 1] - 1 + m) % (2 * k - i).
这个公式其实就是在求杀掉一个人后,下一个被杀的人的编号(也可以说位置,我们关心的就是这个)。
为何是 f[i - 1] - 1?你想想新的环就是一个数组中间删掉了一个元素,他后面的元素一次往左移动一个位置。
移动后,对于新一轮的被杀游戏,开始的位置就是f[ i - 1] - 1的下一个元素,所以就是f[i - 1] - 1. 而分母(2 * k - i)为当前轮的总人数。
要是看不懂(我表达能力还待提高~~),就google、百度吧,这是一大法宝。
(留下评论也行,咱们一起讨论、讨论,互相学习,我也是goole之后才懂的这题的方法,然后自己推导出来这个公式)
_提交情况:
一次就AC了(用的打表)。
_收获:
知道了自己的数学是有多差。得好好做做思维的体操才行啊。
-AC Codes:
#include <cstdio>
int array[14] = {0,2,7,5,30,169,441,1872,7632,1740,93313,459901,1358657,2504881};
int main() {
int k;
while (scanf("%d", &k) != EOF && k) {
printf("%d\n", array[k]);
}
}
/*
上面数组的结果来自这串代码:
#include <cstdio>
//#define GANGAN
int main() {
#ifdef GANGAN
freopen("1012_in.txt", "r", stdin);
freopen("1012_out.txt", "w", stdout);
#endif
int i, m, k;
while (scanf("%d", &k) != EOF && 0 != k) {
int m, flag;
for (m = k; ; m++) {//以0为起始编号能够避免讨论m为(2 * k)时的麻烦
flag = 1;
int killed = 0;
for (int i = 0; i < k; i++) {
killed = (killed - 1 + m) % (2 * k - i);
if (killed < k) {
flag = 0;
break;
}
}
if (flag) {
break;
}
}
printf("%d,", m);
}
return 0;
}
//*/