HDU ACM Step 2.2.2:http://acm.hdu.edu.cn/game/entry/problem/show.php?chapterid=2§ionid=2&problemid=2
题目大意是给一个k,前k个是好人,后k个是坏人,依次不回归数数,数到m的处决,要求找到一个最小的m,使坏人们死在好人前面。
这是个约瑟夫环问题。开始想用数组模拟,死掉的出队,但是超时。后来发现前k个基本不动,完全可以不用数组,直接用下标表示。经过几次超时,进行了如下修改:去掉函数直接写在main函数中,用a[k]保存结果,去掉continue采用if-else。最后才AC,真是艰苦。
源代码:
#include <stdio.h>
int a[14]={0};
int main()
{
int k,i,n,j;
while(scanf("%d",&k)&&k)
{
if(a[k])
printf("%d\n",a[k]);
else{
for(i=k+1;;++i){
//n表示当前活的人数,j表示开始数数的人,i表示m
for(n=k<<1,j=0;n>k;--n)
{
j=(j+i-1)%n;//循环,点到的那个人
if(j<=k-1)
n=0;
}
if(n==k){
a[k]=i;
printf("%d\n",i);
break;
}
}
}
}
return 0;
}
因为k<14,其实完全可以打表:
#include<stdio.h>
int n;
int a[20] = {0,2,7,5,30,169,441,1872,7632,1740,93313,459901,1358657,2504881,0};
int main()
{
while(scanf("%d",&k)&&k){
printf("%d\n",a[k]);
}
return 0;
}
拓展
约瑟夫环问题:n个人,编号0~n-1,从0开始报数,报到m-1的退出,剩下的人继续从0开始报数。求胜利者的编号。
可以采用动态规划方法来做。a[i]表示i个人中最后获胜者的编号,那么在0~i中,m-1首先退出,剩下的编号可以变一变。假设n=5,m=3如下:
0 1 2 3 4→2出队
0 1 3 4→编号重排
2 3 0 1→这显然是a[i-1]的子问题
由于a[4]=0,而0→3,可以得到a[5]=3,考虑2号出队后的编号重排,实际上就是a[4]+3。所以可以得到公式
a[0]=0
a[i]=(a[i-1]+m)%n
源代码:
#include <stdio.h>
#define N 20
int main()
{
int i,n,m;
int a[N]={0};
while(scanf("%d %d",&n,&m))
{
a[1]=0;
for(i=2;i<N;i++)
a[i]=(a[i-1]+m)%n;
}
return 0;
}