Matrix 22程设I-周18-课后2 守望者的逃离

目录

题目: 

思路: 

代码:


题目: 

5d186d61a77f480e95e8c7d4b236167c.png

笔者最开始的思路: 

 守望者有两种移动方式,按照题意,肯定要选择一种最快的移动方式

选择哪一种?我们可以先允许时间为非整数秒,算一下两种移动方式的速度:

第一种:跑步,17m/s

第二种:如果有足够的能量,不需要休息恢复,就是60m/s,如果需要休息恢复,10点能量需要2.5秒恢复,移动一次60米,需要1秒,平均速度60/(2.5+1)约为17.14m/s。

这说明1、就平均速度来看,用闪烁比跑步快,整一个过程中,非特殊情况应当尽可能都用闪烁。2、17.14m/s和17m/s相差很小,由于题目对时间为整数秒的限定,在能量不足并且临界状态下的个别几次应当选择跑步

何为临界状态?可能是只剩下几秒,如果休息充满能量岛就沉没了,几秒内能跑出去,或是剩余的距离很短,闪烁不如跑步快。

因为整数秒的要求,闪烁的时间计算不能直接用17.14m/s,应当根据能量值(用M表示)分情况讨论

M>=10 :1秒移动60米,速度60m/s

6<=M<=9:用1秒充4个能量,然后用1秒移动60米,速度30m/s

2<=M<=5:用2秒充8个能量,然后用1秒移动60米,速度20m/s

0<=M<=1:用3秒充12个能量,然后用1秒移动60米,速度15m/s

需要注意,0<=M<=1时,速度比17m/s小,但不意味着遇到这种情况都采用跑步,因为充能并用一次闪烁M会变成2或3,计算这两个过程的速度,为7秒移动120米,速度17.14m/s,还是比17m/s快的。除非是临界状态,闪烁完一次就结束了,不需要进行第二次闪烁,这时就适合采用跑步。

举个例子吧,题目的第一个测试用例,能量M=39,距离S=200,时间T=4,用dis表示已走的距离,用t表示已经过的时间

MS-disdisT-tt
39200040
92018013
9319704

结果是NO,最大距离为197米。

题目中的这个样例不够典型,对学生的干扰是很大的(可恶的出题人)

我在debug时自己写了一个例子,M=19,S=264,T=15

编号MS-disdisT-tt
1192640150
2920460141
33144120123
418418096
51-1265411

1-2,有超过10个能量,优先用闪烁;2-3,用闪烁,2秒移动60米;3-4,用闪烁,3秒移动60米;4-5,注意这里M=1,并且属于临界情况,如果用闪烁,4秒移动60米,最后24米,跑步2秒完成,还需6秒,如果全都用跑步,只需5秒就能完成85米。

结果为YES,最短时间为11秒。

笔者的代码:

第一步,一直用闪烁把能量消耗到小于10

第二步,进入循环,分别对时间是否足够和M的三种情况讨论

#include<stdio.h>
int main()
{
    int M, S, T;
    while (scanf("%d %d %d", &M, &S, &T) != EOF)
    {
        int dis = 0;
        int t = 0;
        dis += (M / 10) * 60;
        t += M / 10;
        if (dis >= S)//一直用闪烁到逃出的情况
        {
            if (60 * T < S)//时间太短了,一直闪烁不停都逃不出
            {
                printf("No\n");
                printf("%d\n", 60 * T);
                continue;
            }
            else//一直闪烁可以逃出
            {
                printf("Yes\n");
                printf("%d\n", (S - 1) / 60 + 1);
                continue;
            }
        }
        if (t > T)//完成闪烁后未逃出且已超时
        {
            printf("No\n");
            printf("%d\n", T * 60);
            continue;
        }
        M %= 10;
        while (1)
        {
            //时间不够,对M分出3种情况
            if (M >= 6 && M <= 9 && T - t <= 1)
            {
                dis += 17 * (T-t);
                if (dis < S)
                {
                    printf("No\n");
                    printf("%d\n", dis);
                    break;
                }
                else
                {
                    printf("Yes\n");
                    printf("%d\n", T - t);
                    break;
                }
                continue;
            }
            if (M >= 2 && M <= 5 && T - t <= 2)
            {
                dis += 17 *( T-t);
                if (dis < S)
                {
                    printf("No\n");
                    printf("%d\n", dis+8);
                    break;
                }
                else if (dis - 17 >= S)
                {
                    printf("Yes\n");
                    printf("%d\n", t + 1);
                    break;
                }
                else
                {
                    printf("Yes\n");
                    printf("%d\n", t + 2);
                    break;
                }
                continue;
            }
            if (M >= 0 && M <= 1 && T - t <= 3)
            {
                dis += 17 * (T-t);
                if (dis < S)
                {
                    printf("No\n");
                    printf("%d\n", dis);
                    break;
                }
                else if (dis - 17 * 2 >= S)
                {
                    printf("Yes\n");
                    printf("%d\n", t );
                    break;
                }
                else if (dis - 17 >= S)
                {
                    printf("Yes\n");
                    printf("%d\n", t+1 );
                    break;
                }
                else
                {
                    printf("Yes\n");
                    printf("%d\n", t +2);
                    break;
                }
                continue;
            }
            //时间够的情况
            if (M >= 6 && M <= 9&&T-t>1)
            {
                for (int ii = 0; ii <= 1; ii++)//判断是否靠近临界情况
                {
                    if (dis + 17 * ii >= S)
                    {
                        printf("Yes\n");
                        printf("%d\n", t + ii);
                        goto exit;
                    }
                }//非临界情况,使用闪烁
                M += 4;
                t += 2;
                M %= 10;
                dis += 60;
                if (dis >= S)
                {
                    printf("Yes\n");
                    printf("%d\n", t);
                    break;
                }
                continue;
            }
            if (M >= 2 && M <= 5&&T-t>2)
            {
                for (int ii = 0; ii <= 2; ii++)//判断是否靠近临界情况
                {
                    if (dis + 17 * ii >= S)
                    {
                        printf("Yes\n");
                        printf("%d\n", t + ii);
                        goto exit;
                    }
                }//非临界情况,使用闪烁
                M += 8;
                t += 3;
                M %= 10;
                dis += 60;
                if (dis >= S)
                {
                    printf("Yes\n");
                    printf("%d\n", t);
                    break;
                }
                continue;
            }
            if (M >= 0 && M <= 1&&T-t>3)
            {
                for (int ii = 0; ii <= 7; ii++)//判断是否靠近临界情况
                {//这里ii的上界更高,是因为要考虑下一步能否闪烁
                    if (dis + 17 * ii >= S)
                    {
                        printf("Yes\n");
                        printf("%d\n", t + ii);
                        goto exit;
                    }
                }
                M += 12;
                t += 4;
                M %= 10;
                dis += 60;
                if (dis >= S)
                {
                    printf("Yes\n");
                    printf("%d\n", t);
                    break;
                }
                continue;
            }
        exit:break;
        }
    }
    return 0;
}

是不是很复杂?代码长达179行。

提交这个代码后我一直在反思是不是写复杂了,其他的文章给出此题的代码基本都在50行以内,其实我们可以换一种思路。

以下是参考了大佬的思路后写的第二份代码

更优解:

#include<stdio.h>
int main()
{
    int M, S, T;
    while (scanf("%d %d %d", &M, &S, &T) != EOF)
    {
        int t=1,dis1=0,dis2=0;
        for(;t<=T&&dis2<S;t++)
        {
            dis2+=17;
            if(M>=10)
            {
                dis1+=60;
                M-=10;
            }
            else
                M+=4;
            dis2=dis1>dis2?dis1:dis2;
        }
        if(dis2>=S)
        {
            printf("Yes\n");
            printf("%d\n",t-1);
        }
        else
        {
            printf("No\n");
            printf("%d\n",dis2);
        }
    }
    return 0;
}

这种思路只用一个按照时间逐秒递增的for循环,循环到逃出岛屿或时间不足结束

使用dis1和dis2两个变量计算当前时间能前进的最大距离,dis1只记录闪烁的距离,dis2记录跑步的距离,并用dis1更新dis2。

为什么我会把这样简单30行解决的问题写成179行?回想最开始的思路,其实也很简单明了,但写出来以后只能满足少数几个测试用例,当时的我不服气,认为自己的思路是对的,把没通过的情况称为个别的临界情况,需要再加判断条件,找出这些临界情况,并把它们排除,于是我开始了debug。

不知不觉,代码就长了,堆积成山。

建议:如果发现临界情况多,就换一种思路吧,如果没有别的思路,就做下一题吧(特别是考试的时候,如果愿意,平时投入点时间debug也很快乐)。

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值