题意:有k个好人和k个坏人进行约瑟夫环问题,好人在前面,坏人在后面(即好人编号为0...k - 1),求一个最小的m,使他们用m报数时所有坏人在有好人出局之前出局
解法:一开始没怎么细想就写了个模拟……果断T了……于是想把结果打表……结果发现k = 13时根本跑不完……
于是还是枚举m,推导每次出局的人,并在线打表。
以n = 6, m = 5举例:
一开始序列为0, 1, 2, 3, 4, 5
第m%n个人出局,对应的编号为(m + 1) % n
从出局后面的人开始报数,则队列变为:
5, 0, 1, 2, 3重新编号则变为:
0, 1, 2, 3, 4
此时人数n减少了1
则第m%(n - 1)个人出局,对应的编号为(m + 1) % (n - 1)
设上一轮出局的人编号为x,本轮编号从上一轮出局的人的后一人开始,所以反推回去本轮出局的(m + 1) % (n - 1)在上一轮的编号为(x + (m + 1) % (n - 1)) % (n - 1)
化简得到(x + m + 1) % (n - 1)
这是对于第二轮来说的,每轮的人数都会减少,第k轮的时候人数为n - k + 1
所以最终的通项公式为f[0] = 0, f[i] = (f[i - 1] + m + 1) % (n - k + 1),f[i]的含义为第i轮出局的人的编号
那么只要判断前k轮有没有好人出局就可以了,一旦有好人出局则继续枚举m,没有好人出局则为答案
代码:
#include<stdio.h> #include<iostream> #include<algorithm> #include<string> #include<string.h> #include<math.h> #include<limits.h> #include<time.h> #include<stdlib.h> #include<map> #include<queue> #include<set> #include<stack> #include<vector> #define LL long long using namespace std; int main() { int jos[14] = {0};//在线打表,避免反复求解 int n; while(~scanf("%d", &n) && n)//n即为上面说的k,2 * n为上面说的n(变量名起的这么奇幻真的好么) { if(jos[n]) { printf("%d ", jos[n]); continue; } int m = 1; while(1) { int f = 0; int i = 1; for(; i <= n; i++) { f = (f + m - 1) % (2 * n - i + 1); if(f < n) { m++; break; } } if(i > n) { jos[n] = m; printf("%d ", m); break; } } } return 0; }