[仅作为参考] 算法游戏题解

寻宝游戏题解

为了防止“剧透”,关键的部分被折叠起来了。

这个问题很新o,是昨天的Codeforce在线赛的第二分区的A题(一般难度很低)https://codeforces.com/contest/2090/problem/A

这样的文字代表它可以被展开
NeverGonnaGiveYouUp



朴素解法:

  • 用代码模拟这个挖掘过程,在x和y挖完后判断是否达成了深度a

进阶解法:

点击查看进阶解法
  • 一次性求得x与y在最后一轮挖掘前,的剩余深度,然后判断是否足够x有剩余的挖最后一次(被x挖完后如果有剩余,则说明y是最后一次挖掘)




代码与解释

朴素解法

假设我们先从朴素解法出发,因为这个解法不需要任何技巧,只是将操作流程转化为代码而已。

点击展开内容

代码:

#include <cstdio> //c的标准输入输出头文件 (在.cpp中等效于 <stdio.h>)

int main()
{
    int n; 
    scanf("%d", &n);
    while (n--)
    {
        int x, y, a; 
        scanf("%d %d %d", &x, &y, &a);
        int leftToDig = a; 

        while (true)
        {
            // X先挖
            leftToDig -= x; //待挖掘的深度被挖走X
            if (leftToDig + 0.5 < 0) //判断是否被Y先挖完
            {
                printf("NO\n");
                break;
            }

            // Y后挖
            leftToDig -= y; //待挖掘的深度被挖走X
            if (leftToDigeft + 0.5 < 0) //判断是否被Y先挖完
            {
                printf("YES\n");
                break;
            }
        }
    }
}

上述代码是最朴素最基础的解法,

然而很不幸的是,如果在原判题平台上执行这段代码,会因为超时而无法通过大部分案例(最坏有\(O(n*a)\)的复杂度)




进阶解法

解决方案

于是我们需要对操作进行简化:

不难发现,每一轮操作的起点都固定为x先开始,由y结束,而且每次判断退出的条件是一样的。

我们完全可以将两次判断合并成一次判断leftToDig -= (x+y)

然而还不够,这不会减少我们的循环体执行次数,我们需要一个更高效的方法处理这个事情

实际上,在这个十分漫长的判断阶段中,直到leftToDig接近x+y前,因为这个时候跳出条件(leftToDigeft + 0.5 < 0)一定不满足,循环体内部的语句起始都是无效的,

也就是说,实际上只有最末尾的部分(也就是leftToDig<=x+y+0.5的这个区间内)是判断的实际有效范围,
我们只需要找一个办法,让leftToDig快速坍缩到接近x+y+0.5的范围,就可以避免大量循环了

向您介绍:求模运算 (int) a % (int) b

来看代码:

点击查看代码
#include <cstdio>

int main()
{
    int n;
    scanf("%d", &n);
    while (n--)
    {
        int x, y, a;
        scanf("%d %d %d", &x, &y, &a);
        if (a % (x + y) >= x) //注意这里被我们替换成了求模版本的条件
        {
            printf("YES\n");
        }
        else
        {
            printf("NO\n");
        }
    }
}

Wow,代码段被瞬间简化了,时间复杂度也来到了\(O(n)\)范围内。

那为什么可以这么用呢?

大家可能还记得小学二年级学习过的乘法,大概是这样引入的:

我们将 \((a+a+a+a+...+a) (假设一共加n次)\) 简化成 \((a \times n)\)

可以理解为,我们将多次相同的加法操作打包成为乘法

而除法作为乘法的逆运算,也有着类似的性质,也就是:将多次相同的减法操作打包成为除法操作

求模,是一种取余数的方式,也就是获得整数除法中被舍弃的部分的方式

或者换一个思路,我们在这里的运用方式,其实是获得多次相同减法操作后剩余的部分

而且十分“智能”的是,我们刚刚好可以获得最后一次能够完整减去(x+y)之后剩下的部分,就好像我们的leftToDig正正好小于(x+y),一样,我们只需要考虑当前一轮中,x挖完后够不够y这一件事就可以了

于是循环体内部的复杂度被我们从 a 变成了 1,总复杂度就从 \(O(n*a)\) 变成了 \(O(n*1)\)

就可以通过啦 😄






总结

今天的题目难度很低,甚至可以说“一眼盯真”,只是需要进一步思考,优化一下我们的代码而已。

之前的题目都是偏向模拟(以及动态规划)的题,本身提交的代码量就不会低,优化的结果也并不会有极大的提升。(而且有想优化的时间,都可以敲完整段代码了)

posted @ 2025-03-24 12:02  Brakeintime  阅读(46)  评论(1)    收藏  举报