| Time Limit: 1000MS | Memory Limit: 10000K | |
| Total Submissions: 47657 | Accepted: 17949 |
Description
Input
Output
Sample Input
3 4 0
Sample Output
5 30
Source
约瑟夫问题的变形,开始对题意就理解了好久,这类题目主要要找到中间的公式;这道题就要知道 当前出局的位置 = (前一个出局的位置+m-1)%(2k-(k-1);
是从0开始数,这就是一个通式;明白了这个公式这个题相当于就做出了一半;这中间还有一个判别的条件,就是如果当前的位置如果<k,说明这种情况就不满足了;
第一种方法:就直接可以对m的值进行枚举,利用判别条件,找出合适的m值;
下面是代码;这种思路好像200ms可以过;
//暴力+枚举
#include <cstdio>
#include <cstring>
int main()
{
int k;
int ans[20];//储存当前的位置
int Joseph[20]={0}; //储存符合条件的m的值
for(k=1;k<14;k++)
{
int m=1;
memset(ans,0,sizeof(ans));
for(int i=1;i<=k;i++)
{
ans[i]=(ans[i-1]+m-1)%(2*k-i+1); //递推公式
if(ans[i]<k)
{
i=0;
m++;
}
}
Joseph[k]=m;
}
while(scanf("%d",&k)&&k!=0)
{
printf("%d\n",Joseph[k]);
}
return 0;
}要想0ms过的,直接得出结果数组,然后再提交;
//直接枚举 0ms
#include <cstdio>
#include <cstring>
int main()
{
int k;
int Joseph[]={0,2,7,5,30,169,441,1872,7632,1740,93313,459901,1358657,2504881,1245064};
while(scanf("%d",&k)&&k!=0)
{
printf("%d\n",Joseph[k]);
}
return 0;
}
第二种方法,要挖掘题目中的隐含条件,我看了好久都没看出来,代码是参考别人的,这种46ms就可以过;优化了不少;
题目中的隐含条件;只剩下一个坏人的时候,下一个报数的人要么是第1个人,要么是第k+1个人;所以间隔就是m=s*(k+1)或者是 m=s*(k+1)+1;
粗略的证明一下,参考别人的:(这里我也还有点没懂)
仅剩一个坏人,圈长为k+1;设下一个报数人为第一个人时的时间间隔为m1;下一个报数人为第k+1个人时的时间间隔为m2;
由 (1+m1)%(k+1)=1 得出 m1= s*(k+1);
(k+1+m2)%(k+1)=1 得出 m2=s*(k+1)+1;
这样我们就可以对m的枚举优化剪枝,m是(k+1)的倍数或者是他的倍数+1;
#include <cstdio>
#include <cstring>
bool Joseph(int k,int m)
{
int n=2*k,x=0;
while(n>k)
{
x=(x+m-1)%n;//递推公式,算出当前出局的位置
if(x<k) //判别条件
return false;
n--;
}
return true;
}
int main()
{
int k;
int result[20]={0};
for(k=1;k<14;k++)
{
for(int i=k+1;;i+=k+1) //对m枚举进行优化
{
if(Joseph(k,i)) //m是k+1的倍数或者倍数+1
{
result[k]=i;
break;
}
else if(Joseph(k,i+1))
{
result[k]=i+1;
break;
}
}
}
while(scanf("%d",&k)&&k)
{
printf("%d\n",result[k]);
}
}
原文地址:http://blog.csdn.net/whjkm/article/details/37990217