题目大概是这样的:
n只猴子(n不超过50)围成一个圈。从某一只开始依次给猴子们编号,从1到n。然后从第一只猴子开始,从1开始依次报数,报到m的猴子离开圈子。从这只离开的猴子的下一只开始再从1开始报数,报到m的再离开。以此类推,直到最后剩下一只猴子为止。这只剩下的猴子就是大王。现在编写程序,输入n和m,输出大王的号码。
举个例子,假设n等于4,m等于三。最开始圈子里面有4只猴子:
1 2 3 4
从1号猴子开始报数,报到3的猴子离开。于是现在剩下的猴子是:
1 2 4
然后从4号猴子报1,1号猴子报2,2号猴子报3离开。于是剩下:
1 4
然后从4号猴子报1,1号猴子报2,4号猴子报3离开。这样剩下一只1号猴子就是大王。
这种解决问题的方法叫模拟。如果数字小,可以手工模拟。如果数字大,让人来做模拟,工作量太大,而且容易出错。但是计算机最擅长解决这样的单调重复的问题了,所以我们编个程序来模拟这个过程。
首先先想一下如何模拟这个站在圈子里和离开圈子的过程。我们可以申请一个数组,数组的一号下标代表一号猴子,50号下标代表50号猴子。由于题目中限定猴子不会超过50只,所以只要声明长度为51的数组就够了。C语言里面的数组下标是从0开始的,零号我们弃置不用,最多从1号使用到50号下标。在报数之前把数组有猴子的部分初始化成都是1.用1代表这只猴子还在圈里。如果这只猴子离开圈子,就把数组这个格子写入0.这样,刚才我们说的模拟过程就可以用数组表示成这样:
最开始:第一行表示数组下标,第二行表示数组里面的值。只写出0到4号下标的情况,后面全是0省略不写。
0 1 1 1 1
第一轮报数之后:
0 1 2 3 4
0 1 1 0 1
第二轮报数之后:
0 1 2 3 4
0 1 0 0 1
第三轮报数之后:
0 1 2 3 4
0 1 0 0 0
于是还剩下1号是1,那么一号猴子就是大王。
那么,这个程序的思路就是:
读入n和m
把数组的1到n号下标赋值成1
报数n-1次,报数的时候维护数组,把合适的位置写成0
找到数组里面剩下的唯一不是0的位置。把这个位置的下标输出。
下面我们把注意力集中在如何报数和维护数组的问题上。为了方便起见先编写两个小函数。
第一个函数的名字叫succ,给它一个数字,它返回这个参数在这个圈子里面的下一个数。就是说,如果n等于10,你给它7它就返回8,你给它8它就返回9.但是,注意,如果你给它10,它会返回1.这样,就相当于用这个函数实现了循环数组。一旦你数到了数组的结尾,那就自动再从开头开始数。这样的函数是不是很好写啊。
{
if(t==n)
return
1;
return
t+1;
}
注意两个问题。第一,这个n是全局变量,就是定义在所有函数外面,所有函数公用的变量。第二,可能有人问我为什么这个if语句不用else。仔细分析一下,如果t和n相等,那么就直接返回1,结束了,就不会执行return
t+1这句话。如果能执行到return
t+1,就说明前面的t==n的条件不成立。所以这里面不需要else。当然,如果你写成这样也是等价的:
int succ(int t)
{
if(t==n)
return
1;
else
return
t+1;
}
下一个函数是给它一个数字,它返回从这个位置往后,下一个在圈子里的猴子的号码。先贴代码,然后解释:
int nextMonkey(int t)
{
int
i;
i=succ(t);
while(a[i]!=1)
i=succ(i);
return
i;
}
首先通过i=succ(t);这句话,找到t开始下一只猴子的位置(不管这一只在不在圈子里),然后判断循环,如果a[i]!=1,就是说这一只已经离开圈子了,那么i=succ(i),再找下一只猴子。就这样循环,知道找到一只在圈子里的为止。这样,就找到了从t后面开始第一只在圈子里的猴子,这只猴子的号码是i。于是返回i就可以了。
有了这两个函数做铺垫,是不是一切简单多了?我把完整代码贴上来,然后简单解释一下:
int succ(int t);
int nextMonkey(int t);
int n,m;
int a[51];
int main(void)
{
int
i,j,p;
scanf("%d%d",&n,&m);
for(i=1;i<=n;i++)
a[i]=1;
p=1;
for(i=1;i
< n;i++)
{
for(j=0;j
< m-1;j++)
{
p=nextMonkey(p);
}
a[p]=0;
p=nextMonkey(p);
}
printf("%d",p);
return
0;
}
int succ(int t)
{
if(t==n)
return
1;
return
t+1;
}
int nextMonkey(int t)
{
int
i;
i=succ(t);
while(a[i]!=1)
i=succ(i);
return
i;
}
首先,这个程序里面n、m和数组a都是全局变量。这样,程序里面的这几个函数都可以用它们。从main函数开始,先读入n和m,然后用一个循环语句,把数组里面从1到n的格子都赋值为1,表面开始的时候所有猴子都在圈子里。然后p赋值为1,表示现在从第一号猴子开始报数。
然后进入一个两重循环,外层用i控制,循环n-1次,代表n-1次报数。里层先循环m-1次,用j控制,每次都向后数一个还在圈子里的猴子。这样,当m-1次循环结束之后,我们找到了不幸的报到m号的猴子,这只猴子的号码是p。于是把p位置清零,然后往后找一只猴子,继续下一次报数。当外层循环结束的时候,剩下一只,号码就是p了,于是把p输出。
有两个问题我没有说具体。为什么里层循环是m-1次而不是m次?为什么外层循环结束的时候剩下的大王号码就是p?自己好好思考哦。