http://acm.tju.edu.cn/toj/showp1007.html
这个题好像是一个约瑟夫环变形的题,乍一看没什么思路,后来在网上参考解题方法,找到了一个比较不错的思路。
大体思路是,规定好人坏人各k个,那么假设总人数就是2*k,用j指向将要杀死的那个人,从0开始代表第一个人,因为先要杀死全部的坏人,所以m一定是大于k,从k+1开始。注意下面这个式子:
j = (j+m-1)%sum;
这个式子是计算j的公式,j+m-1代表指向的第几个人(从0开始所以减一),再对sum取余。同时sum--,表示杀死了一个人。如果好人被杀死,那么说明这个m不符合要求,退出内层循环,m++;如果只剩下了好人,代表坏人都被杀死了,符合要求,同样退出内层循环,返回m值即可。
值得注意的是,当我直接返回m时,提交之后报错(Time Limit Exceed),后来我改成用数组存储每一个k对应的m值,成功AC。在网上查了一下,发现这种方法叫做“打表”,大概就是把所有可能的结果都存储在一个数组中,用的时候直接取出来。这种方法适用于数据范围小但是计算量大的情况,对应于这道题则刚好适用。
代码:
#include <stdio.h>
#include <stdlib.h>
int result[15];
int Joseph(int k){
int sum;
int m, flag;
if(result[k]) return result[k];
for(m = k+1; ;m++){
sum = 2*k;
int j = 0; //当前指向的人,0代表第一个
while(1){
j = (j+m-1)%sum;
sum--; //人数少一个
if(j < k){ //代表杀死好人的情况
flag = 0;
break;
}
if(sum == k){ //代表只剩下好人
flag = 1;
break;
}
}
if(flag){
result[k] = m;
return result[k];
}
}
}
int main(){
int k;
while(scanf("%d", &k) && k != 0){
printf("%d\n", Joseph(k));
}
return 0;
}