您的位置:首页 > 其它

POJ 1012 Joseph

2011-03-18 20:32 363 查看
考验思维能力的一道题

一开始用了最笨的方法,一个一个数,结果k到10就跑不出来了

后来看了discuss,可以推公式,这样就可以直接推得下一个杀人的位置,不必一个一个数了,时间可以节省非常多,自己找了小点的k尝试了下,推出了公式,我们从点1开始编号,则初始情况一共有2*k个点

假设b为已经杀掉的人数,now为现在的起始位置,next为下一个杀掉人的编号,初始时,b=now=0

则第一步,next=(0+m-1)%(2k-0)+1,(减1取模后再加1,为了防止0的出现),编号为next的人被杀掉,注意,此时的now不是next,而是next-1!然后再循环找下去就行,next=(now+m-1)%(2*k-b),由于不需要知道被杀掉的人原来的编号,杀掉这个人后,我们可以把整个环的数量减1,而所有坏人和好人都是还是各自连在一起的,所以不会有影响

后来看了discuss后,经牛人点拨,得知m必定为k+1的整数倍或者k+1的整数倍加1,为什么呢?考虑只剩下两个坏人时,他们此时的编号为k+1和k+2,如果杀掉的是k+1,那么走m=n*(k+1)+1步后可以杀掉原来编号为k+2的人,而如果先杀掉的是原来编号为k+2的,那么走m=n*(k+1)步后可以杀掉原来编号为k+1的人。这个解释太精彩了!这样优化后,可以从200多ms跑到100ms左右

总结:做题就是这样的,先从最笨的方法开始,然后在此基础上进行优化,另外,对于一些规律性的东西,可以从特殊情况或者最简单的情况去挖掘信息!还有,这题有很多组测试数据,但最多13种情况,我们可以将某种情况算出来的结果保存下来,下次再测试时直接输出!

代码:

#include<iostream>
#include<memory.h>
#include<string>
#include<cstdio>
#include<algorithm>
#include<math.h>
#include<stack>
#include<queue>
using namespace std;
int f[100];
bool go(int m,int k)
{
int i,j,cnt,now,next,b=0;
cnt=j=0;
i=-1;
now=0;
while(true)
{
//cout<<"now="<<now<<endl;
next=(now+m-1)%(2*k-b)+1;
//cout<<"next="<<next<<endl;
if(next<=k)
return false;
else
{
now=next-1;//注意,杀掉的人被删除了,起点要减1
b++;//删掉的人数加1
cnt++;
if(cnt==k)
return true;
}
}
}
int main()
{
int i,j,cnt,k,m,flag;
memset(f,0,sizeof(f));
while(scanf("%d",&k)!=EOF)
{
if(!k)
break;
if(f[k])
{
cout<<f[k]<<endl;
continue;
}
for(m=k+1;;m++)
{
//int t=m%(2*k);//m=i*(2*k)+j,i为整数,k<j<=2*k
if(m%(k+1)>1)//m为k+1的整数倍,或者k+1的整数倍加1
continue;
//if(t&&t<=k)
//continue;
if(go(m,k))
break;
//if(t==0)
//m+=k;
}
f[k]=m;
cout<<m<<endl;
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: