题目大意:给定一个k,代表有k个好人和k个坏人。按照约瑟夫问题的规则进行。求出最小的m,使经过k轮后,k个坏人全部被杀死。
思路:
(1)先给出约瑟夫环的递推公式:f(i)=(f(i-1)+m-1)%(n-i+1),f(0)=0;其中n代表开始时共有n人,m代表每次杀死第m个人,f(i)代表按照第i轮顺序编号,第i轮应该杀死的人的编号。按照题目要求,好人的编号始终都是0~k-1,所以一旦f(i)<k ,则不符合要求。
(2)递推公式的证明如下:令第i-1轮杀死的人的编号为f(i-1),则第i轮从f(i-1)+1开始,此时场上有n-i+1个人。存在映射:
f(i-1) --> 0 ; f(i-1)+1 --> 1 ; ...... ; f(i-1)-2 --> n-i-1 ; f(i-1)-1 --> n-i;
按照后者的编号,应该杀死的人的编号f ‘(i) = (m-1) % (n-i+1); 则 f(i) = f'(i) + f(i-1)%(n-i+1) = (f(i-1)+m-1)%(n-i+1);
(3)令答案为m,则m应该大于等于k+1进行枚举。另外,m不必递增枚举,m必须满足k+1的整数倍或者k+1的整数倍加1。证明如下:考虑第k-1轮时,此时场上还剩下k个好人和1个坏人。此时只存在两种序列 GGGG.....GGXB 或 GGGG......GGBX,显然m = t(k+1) + b,t=1,2,3....,b=0或1。
代码,服务器端打表
#include <iostream>
#include <algorithm>
using namespace std;
bool test(int m,int k)
{
int n=k*2,i,j=0;
for (i=1;i<=k;i++)
{
j=(j+m-1)%(n-i+1);
if (j<k)
return false;
}
return true;
}
int main()
{
int k,ans[14],j;
for (k=1;k<=13;k++)
{
for (j=1;;j++)
{
if (test(j*(k+1),k))
{
ans[k]=j*(k+1);
break;
}
if (test(j*(k+1)+1,k))
{
ans[k]=j*(k+1)+1;
break;
}
}
}
while (scanf("%d",&k)==1 && k!=0)
printf("%d\n",ans[k]);
return 0;
}