题目链接:http://poj.org/problem?id=1012
我们从点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<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int f[100];
bool go(int m,int k){
int cnt=0,now=0,next,b=0;
while(true){
next=(now+m-1)%(2*k-b)+1;
if(next<=k)
return false;
else{
now=next-1;
b++;
if(b==k)
return true;
}
}
}
int main(){
int k;
while(~scanf("%d",&k)){
if(!k)
break;
if(f[k]){
printf("%d\n",f[k]);
continue;
}
int i;
for( i=k+1;;i++){
if(i%(k+1)>1)
continue;
if(go(i,k))
break;
}
f[k]=i;
printf("%d\n",i);
}
}