博主不定期更新【保研/推免、C/C++、5G移动通信、Linux、生活随笔】系列文章,喜欢的朋友【点赞+关注】支持一下吧!
一群猴子要选新猴王。新猴王的选择方法是:让N只候选猴子围成一圈,从某位置起顺序编号为1~N号。从第1号开始报数,每轮从1报到3,凡报到3的猴子即退出圈子,接着又从紧邻的下一只猴子开始同样的报数。如此不断循环,最后剩下的一只猴子就选为猴王。请问是原来第几号猴子当选猴王?
输入格式:
输入在一行中给一个正整数N(≤1000)。
输出格式:
在一行中输出当选猴王的编号。
输入样例:
11
输出样例:
7
思路1
以输入样例N=11为准,列出完整其淘汰过程,寻找规律(在能找到规律的前提下,也可以将N设的更小一点):
初始值 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 |
---|---|---|---|---|---|---|---|---|---|---|---|
第一轮后 | 9 | 10 | × | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
第二轮后 | 6 | 7 | × | 8 | 9 | × | 1 | 2 | 3 | 4 | 5 |
第三轮后 | 3 | 4 | × | 5 | 6 | × | 7 | 8 | × | 1 | 2 |
第四轮后 | × | 1 | × | 2 | 3 | × | 4 | 5 | × | 6 | 7 |
第五轮后 | × | 5 | × | 6 | × | × | 1 | 2 | × | 3 | 4 |
第六轮后 | × | 2 | × | 3 | × | × | 4 | 5 | × | × | 1 |
第七轮后 | × | 4 | × | × | × | × | 1 | 2 | × | × | 3 |
第八轮后 | × | 1 | × | × | × | × | 2 | 3 | × | × | × |
第九轮后 | × | 1 | × | × | × | × | 2 | × | × | × | × |
第十轮后 | × | × | × | × | × | × | 1 | × | × | × | × |
依据题意,每次报数为3的猴子被淘汰,之后更新所有猴子序号,继续进行,整个过程如上表所示。每一轮淘汰一只猴子,因此共需要N-1
轮,即可选出猴王。下面来看一下淘汰过程中,序号的更新规律:淘汰序号为3的猴子后,原队列中序号大于3的猴子,序号-3;原队列中序号小于3的猴子,序号+(N-3),且N每轮结束后要递减
。我们依据此规律编写代码,有以下需注意的点:
1. 当进入倒数第二轮(即第N-2轮)时,序列中只有两个序号,1和2,此时不能再以序号=3作为出局的判断条件;而观察数据可以发现,在第N-3轮时,剩余的三个猴子中,序号为2的便是最终的获胜者,因此,为了每次淘汰的条件一致,我们只进行N-3轮淘汰,而在剩下的3个猴子中,序号为2的便是猴王;
2. 由于我们要进行N-3轮淘汰,因此当N=1,2或3时,无法进行淘汰,而根据之前制定的序号为2即是猴王的规则,我们发现,N=2和3时,都恰好能给出正确答案,而N=1时输出为空,因此,对于N=1的特殊情况,我们单独列出;
3. 对于其中被淘汰的猴子,暂时没有想到什么好的方法来表示,这里给每个被淘汰的猴子序号赋值为-1000000(即最大N×N的相反数),以确保经过循环的运算,不会对输出结果造成影响;
#include <stdio.h>
int main()
{
int N;
scanf("%d", &N);
int n=N; //由于每轮判断需要对N进行递减,在此我们保留原始N值,做循环以及输出用
int a[N];
int i;
/*读入猴子序号*/
for (i=0; i<N; i++)
{
a[i]=i+1;
}
/*进行N-3轮淘汰*/
int j;
for (j=1; j<=n-3;j++)
{
for (i=0; i<n; i++)
{
if (a[i] == 3) //每轮淘汰以序号是否=3作为判断条件
{
int t=0;
/*按更新规律对序号进行更新*/
for (t=0; t<n; t++)
{
if (a[t]<a[i])
{
a[t] = a[t] + N-a[i];
}
else if (a[t]>a[i])
{
a[t] = a[t] - a[i];
}
}
/*每一轮更新完成后,将淘汰猴序号设为如下,N递减,跳出内层循环,进行下一轮淘汰*/
a[i] = -1000000;
N--;
break;
}
}
}
/*N=1特殊情况,单独输出*/
if ( n==1 )
{
printf("%d\n", n);
}
/*N=2和3时,原本也应单独输出,但考虑到与以下循环结果一致,因此不单独给出*/
/*N-3(N>3)轮淘汰后,剩下的3只猴子中,序号2为猴王*/
for (i=0; i<n; i++)
{
if (a[i] == 2)
{
printf("%d\n", i+1);//猴子序号等于数组下标+1
}
}
return 0;
}
思路2
以上解法中淘汰过程中直接用猴子的序号作为判断依据,每次淘汰完都需要对每个猴子的序号进行更新,且被淘汰的猴子序号不知道如何处理,方法较为复杂,更像是在做数学题而不是编程题。下面给出更简单直观的方法,引入两个计数变量,第一个变量作为淘汰变量,每数到3淘汰一个,重新计数;第二个变量作为剩余变量,记录剩余猴子数,每淘汰一个减少1,当剩余变量为1时,结束
,代码如下:
#include <stdio.h>
int main()
{
int N;
scanf("%d", &N);
int a[N];
int i;
for (i=0; i<N; i++)
{
a[i]=i+1;
}
int elimination = 0; //淘汰变量
int remainder = N; //剩余变量
while (remainder > 1)
{
for (i=0; i<N; i++)
{
if (a[i] == 0)
{
continue;
}
//序号为0的猴已淘汰,不计数,直接进入下一轮循环
elimination++;
if (elimination == 3)
{
a[i] = 0; //淘汰之后的猴子序号设为0
elimination = 0; //淘汰变量重置
remainder--; //剩余猴数减1
}
}
}
for (i=0; i<N; i++)
{
if (a[i] != 0)
{
printf("%d\n", i+1);
}
}
return 0;
}