约瑟夫问题是个有名的问题:m个人围成一圈,从第一个开始报数,第n个将被杀掉,最后剩下一个,其余人都将被杀掉。例如N=6,M=5,被杀掉的顺序是:5,4,6,2,3,1。
简单的方法是用数组做一个模拟,一遍一遍的扫过数组,每经过n个没死的人,数组赋值1表示死亡,直至死亡数等于m,输出最后的人编号。
#include<stdio.h>
int array[1000];
int main(int argc,char* argv[])
{
int m,n;
scanf("%d%d",&m,&n);
int k=0,s=0,died=0;
do
{
k++;
if(k>m)
k=1;
if(!array[k])
s++;
if(s==n)
{
s=0;
array[k]=1;
died++;
}
}while(died<m);
printf("%d\n",k);
return 0;
}
***用数组模拟死亡过程速度太慢了,在网上搜到了快得多的数学方法……
递归的方法,把每个人的编号先都减一,这样会是0~(m-1)号。
设第一个死掉的是 ( n-1 )%m 号,所以说之后第二次选谁死的时候,第一个人的编号 0 就是原来的 n%m ,第二个死掉的人的编号 x2 的原编号就是 ( n%m+x2 )%m = ( n+x2 )%m,知道了原理,但这样为什么不会出错呢?不应该剔除掉死掉的家伙吗?因为 x2 肯定是小于 m 的,加起来取余后肯定在上一个死掉的人编号前面,所以不会出现算进去已经死掉的人的问题!
按照这个思想的话,最后一个死掉( 后面特指'他' )的肯定是编号是0的,因为只剩他自己了吗,所以说这个问题就神奇的转换成了他原来的编号是多少?!!依次迭代即可。
迭代公式 f ( x ) = ( f ( x-1 ) +n ) % x ( 1<x<=m ) ,f ( 1 ) = 0
#include<stdio.h>
int main(int argc,char* argv[])
{
int m,n;
scanf("%d%d",&m,&n);
int s=0;
for(int i=2;i<=m;i++)
s=(s+n)%i;
printf("%d\n",s+1);
return 0;
}
非常巧妙的方法~
END