題目大意:給定一個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;
}