n个人围成圈报数游戏

n个人围成圈报数游戏

n个人围成圈报数游戏

Description
有n个人围成一圈,顺序排号。从第一人开始报数(从1到3报数),凡报到3的人退出圈子,问最后留下的是原来第几号的那位。

Input
一行,一个数n。

Output
输出最后留下来的人的序号。

Sample Input 1 
40

Sample Output 1
28

来源:CP1266 n个人围成圈报数游戏(难度:中等)

一、前期:一头雾水

看到这个题目时,我先在草稿纸上画了画,感觉写起来可能有点难度。
但是,真正打开Clode Blocks时,我才真正面对了它。
看着“Hello World”,我想开始写,却发现无从下手:
怎么让报数3的人走开?
想不出来这个问题,我连一个“int”都打不出来。
这个问题难住了我。

二、中期:希望渺茫

循环报数,已经走开的人不参与接下来的报数。
我感觉这就是这个题的难点。
想出来,就能做出来;想不出,就做不出来。
我想了十分钟,仍旧是找不到方向。
我想过上网查询,但却又不甘心。
这时,我想到一个理论:

做不出来一道题时,可以先放一放,先做后面的题,做着做着前面的那个题也就能做出来了。

于是我先去做了它后面的那道题:CP1487用指针实现数据交换(难度:简单)
呃……四五行的代码……
再后面,就不是指针的题了。
于是我又回到了这道题。

三、后期:峰回路转

其实我还是没有做出来。
并不存在说,做一道简单题后,就能做出中等题的说法。
于是我去吃了个饭。
吃完饭回来,灵光一闪……不对。
没有灵光。
我还是只能在草稿本上写写画画,希冀那一刹那的灵光。
它来了。
没有来由地,我突然想到:如果把报数3的数字赋值为0,会怎么样?
这就是正解。

点击查看代码
#include <stdio.h>
#include <stdlib.h>

int main()
{
    int i,count=0,n,remain,number[100];
    scanf("%d",&n);
    remain=n;
    for(i=0; i<n; i++)
    {
        number[i]=i+1;
    }
    while(remain>1)
    {
        for(i=0; i<n; i++)
        {
            if(number[i]!=0)
            {
                count++;
                count=count%3;
            }
            if(count==0)
            {
                number[i]=0;
            }
        }
        remain=0;
        for(i=0; i<n; i++)
        {
            if(number[i]!=0)
                remain++;
        }
    }
    for(i=0; i<n; i++)
    {
        if(number[i]!=0)
            printf("%d\n",number[i]);
    }
    return 0;
}

但是啊,这里面并没有用到指针。

四、尾声

我终究是做了出来。
指针嘛,程序都出来了,插进去还不简单?
——————————
有点烂尾……
可能是我太菜了,但我确实花了很长时间才想出来0这个操作……
另外,前面的小故事,看看就好,不要笑我(* /ω\*)
下面是思考与总结。

五、回归问题本身

在做出来后,我上网查询了一下这个问题,它是C语言经典例题(还有名字“约瑟夫问题/约瑟夫环”),并且网上有很多讲解,也有多种方法。
回忆我做这道题时的想法,发现其实赋值0也可以改为赋值-1之类的,只要赋同一个值,非正数就行。
总之,通过这道题目,我领悟到了两个小妙招:
1.“离开/退出”:可以通过赋值0/负数;
2.“围成一圈”:可以通过设置一个变量,在循环到第一个值时继续++就行。

六、另解

点击查看代码
#include <stdio.h>
#include <stdlib.h>

int main()
{
    int number[100],n,i,pointer=0,count=0,remain;
    scanf("%d",&n);
    remain=n;
    for(i=0; i<n; i++)
    {
        number[i]=1;
    }
    do{
        pointer++;
        if(pointer>n)
            pointer%=n;
        if(number[pointer-1]==1)
        {
            count++;
            if(count==3)
            {
                number[pointer-1]=0;
                count=0;
                remain--;
            }
        }
    }while(remain!=1);
    for(i=0; i<n; i++)
    {
        if(number[i]==1)
            printf("%d",i+1);
    }
    return 0;
}

受到魔术师这题的启发,我想到可以使用类似的思维处理该题。
优点:在检测到存储数字是0后,直接跳过(不报数),而非报上一个人的数,因此可以直接在报数3时减少剩余人数,而不需要在所有人报完一轮后,统计剩余人数。
以下是AI对比两个代码的优略:

对于以上两个代码的比较,我们可以从以下几个方面进行评估:

1. 代码逻辑和可读性:
   - 第一个代码使用了嵌套的for循环来实现报数和删除操作,代码逻辑相对简单,易于理解。
   - 第二个代码使用了do-while循环,并使用了指针来表示当前报到的人,在代码逻辑上更加清晰。

2. 性能:
   - 第一个代码在每次删除一个人后,需要重新计算剩余人数,可能导致性能损失。
   - 第二个代码使用了remain变量来记录剩余人数,并使用了pointer指针来表示当前报到的人,可以减少循环次数,性能上可能更优。

3. 内存占用:
   - 第一个代码使用了一个数组number来表示每个人的状态,占用了一定的内存空间。
   - 第二个代码同样使用了一个数组number来表示每个人的状态,但同时还使用了一个pointer变量,相对而言可能占用更多的内存空间。

4. 输入验证和异常处理:
   - 两个代码都缺乏输入验证和异常处理的代码,可能在输入错误数据时出错。

综上所述,第一个代码的优点在于逻辑简单、易于理解,代码量较少;第二个代码的优点在于逻辑清晰,可能在性能上略有优势。然而,两个代码都需要添加输入验证和异常处理的代码,以保证程序的稳定性和可靠性。根据具体需求和对代码可读性、性能和内存占用的要求,选择适合的代码实现。
posted @ 2023-12-02 20:25  20231420  阅读(70)  评论(2编辑  收藏  举报