【1654. 到家的最少跳跃次数】

news2024/11/25 18:41:24

来源:力扣(LeetCode)

描述:

有一只跳蚤的家在数轴上的位置 x 处。请你帮助它从位置 0 出发,到达它的家。

跳蚤跳跃的规则如下:

  • 它可以 往前 跳恰好 a 个位置(即往右跳)。
  • 它可以 往后 跳恰好 b 个位置(即往左跳)。
  • 它不能 连续 往后跳 2 次。
  • 它不能跳到任何 forbidden 数组中的位置。

跳蚤可以往前跳 超过 它的家的位置,但是它 不能跳到负整数 的位置。

给你一个整数数组 forbidden ,其中 forbidden[i] 是跳蚤不能跳到的位置,同时给你整数 abx ,请你返回跳蚤到家的最少跳跃次数。如果没有恰好到达 x 的可行方案,请你返回 -1

示例 1:

输入:forbidden = [14,4,18,1,15], a = 3, b = 15, x = 9
输出:3
解释:往前跳 3 次(0 -> 3 -> 6 -> 9),跳蚤就到家了。

示例 2:

输入:forbidden = [8,3,16,6,12,20], a = 15, b = 13, x = 11
输出:-1

示例 3:

输入:forbidden = [1,6,2,14,5,17,4], a = 16, b = 9, x = 7
输出:2
解释:往前跳一次(0 -> 16),然后往回跳一次(16 -> 7),跳蚤就到家了。

提示:

  • 1 <= forbidden.length <= 1000
  • 1 <= a, b, forbidden[i] <= 2000
  • 0 <= x <= 2000
  • forbidden 中所有位置互不相同。
  • 位置 x 不在 forbidden 中。

方法:广度优先搜索

思路

求最短路径一般需要用广度优先搜索,但是此题中的图是个无限图,如果不限制搜索的范围,无法处理无解的情况。因此,解决此题的关键是找出搜索的范围,其中下限已经由题目给出,不能跳到负整数的位置,我们还需要找出搜索的上限,下面分情况讨论:

  1. a = b。此时为了次数最少,跳蚤没有必要向后跳,只需要一直往前跳。当它超过 x 却没有遇到 x,表示它再也跳不到 x 了,此时的上限可以设置为 x。
  2. a > b。题目规定,跳蚤不能连续往后跳 2 次,因此这只跳蚤运动轨迹中,任意连续的两次跳跃,总的行程一定是在前进的,前进了 a−ba-ba−b 的距离。即使它某一步是在后退,这一步的前一步和后一步(如果有的话)一定是在前进。此时跳蚤运动的上限为 x + b,在这个上限的情况下,跳蚤往回跳一步可以到达 x。在大于这个上限的情况下,即使跳蚤马上往回跳一步,所处的位置也大于 x,而且跳蚤接下来前进的次数必然会大于等于后退的次数,再也无法到达 x。因此在这种情况下,上限为 x + b。
  3. a < b。在这种情况下,上限为 max⁡(max⁡(forbidden) + a + b, x)。接下来证明这一点。为了方便,记 max⁡(forbidden) = f。首先,需要将数轴上大于等于 0 的位置分为三个区域:
    • [0, f],禁止区。所有 forbidden 中的位置都位于这个区域。
    • (f, max⁡(f + a + b, x)],安全区,它的右边界是 a < b 情况下我们想要证明的广度优先搜索的上限。
    • (max⁡(f + a + b, x), +∞) ,界外区。

这三个区域合起来组成了数轴上大于等于 0 的所有部分,注意 x 可能位于禁止区或者安全区,但不会是 forbidden 数组中的元素。假设某个步数最少的路径中,点 C 是第一个进入界外区(前进进入)的点,而点 H 是第一个离开界外区(后退离开)的点。因为 x 只可能位于禁止区或者安全区,因此如果这条路径存在点 C,那么必然存在点 H。如下图,横坐标为步数,纵坐标为与原点的距离。箭头朝右上表示前进,箭头朝右下表示后退。
1

接下来,我们通过交换线段 BC 和线段 GH ,并保持其他线段的的方向不变,来使得点 C 不再位于界外区。如下图,线段 BC’ 变为后退而线段 G’H 变为前进。

2

我们从以下几个方面论证这种交换的可行性:

  • 交换前,点 C,D,…,F,G 全都位于界外区,与原点的距离大于 f + a + b 。通过交换,这些点与原点的距离缩小了 a + b ,仍然大于 f。因此,这些点不会落到 forbidden 中。
  • 交换后不会增加这个路径的步数,也不会影响点 H 之后的点的位置。
  • 交换不会造成两次倒退。交换后,前进的线段 BC 变为后退的线段 BC’ ,但是 BC’ 的前一段 AB 一定是前进的。可以利用反证法证明,如果 AB 是后退的,那么点 A 就会在界外区,因为 a < b ,这样的话点 C 就不会是第一个界外区的点,因此 AB 一定是前进的。BC’ 的后一段 C’D’ 一定也是前进的。这里需要分为两种情况:
    • CD 原本就是前进的,那么 C’D’ 会保持原来前进的方向。通过交换,我们不会造成两次倒退。
    • CD 原本是后退的,那么点 D 就是我们前面讨论的第一个离开界外区的点 H,因为 a < b。这样一来,我们其实是交换了前进的 BC 和后退的 CD,得到了后退的 BC’ 和前进的 C’D,仍然不会造成两次倒退。

通过这样的交换,我们使得一个有效路径第一个进入界外区的点,不再位于界外区。新的路径,第一个进入界外区的点,可能位于点 C 和点 H 之间,也可能位于点 HHH 之后,也可能不存在这样的点。总之,我们可以不停地寻找第一个进入界外区的点,然后经过上述的交换,使得最终的路径的所有点都位于禁止区和安全区。这样,我们就证明出,如果某个输入有解,那么至少有一条最短路径,它的所有点都处于上限 max⁡(f + a + b, x) 之内。因此在这种情况下,上限为 max⁡(max⁡(forbidden) + a + b, x)。

综合以上三种情况,广度优先搜索的上限是 max⁡(max⁡(forbidden) + a, x) + b 。

在进行广度优先搜索时,除了需要注意到上下限,不能达到 forbidden 数组中的坐标,还需要注意到达每个坐标时,都会有前进到达还是后退到达两种状态。如果是前进到达时,下一步可以选择前进或者后退;如果是后退到达时,下一步只能选择前进。因此广度优先搜索的每个元素,需要保存三个信息,坐标,方向和步数。在代码中,我们用 1 表示前进, −1 表示后退,用哈希集合 visited 来记录已经达到过的位置和方向状态。在搜索的过程中,如果坐标第一次为 x,则返回当前步数。当队列为空时,表示 x 不可到达,返回 −1。

代码:

class Solution {
public:
    int minimumJumps(vector<int>& forbidden, int a, int b, int x) {
        queue<tuple<int, int, int>> q;
        unordered_set<int> visited;
        q.emplace(0, 1, 0);
        visited.emplace(0);
        int lower = 0, upper = max(*max_element(forbidden.begin(), forbidden.end()) + a, x) + b;
        unordered_set<int> forbiddenSet(forbidden.begin(), forbidden.end());
        while (!q.empty()) {
            auto [position, direction, step] = q.front();
            q.pop();
            if (position == x) {
                return step;
            }
            int nextPosition = position + a;
            int nextDirection = 1;
            if (lower <= nextPosition && nextPosition <= upper && !visited.count(nextPosition * nextDirection) && !forbiddenSet.count(nextPosition)) {
                visited.emplace(nextPosition * nextDirection);
                q.emplace(nextPosition, nextDirection, step + 1);
            }
            if (direction == 1) {
                nextPosition = position - b;
                nextDirection = -1;
                if (lower <= nextPosition && nextPosition <= upper && !visited.count(nextPosition * nextDirection) && !forbiddenSet.count(nextPosition)) {
                    visited.emplace(nextPosition * nextDirection);
                    q.emplace(nextPosition, nextDirection, step + 1);
                }
            }
        }
        return -1;
    }
};

时间 32ms 击败 44.76%使用 C++ 的用户
内存 18.11MB 击败 43.81%使用 C++ 的用户
复杂度分析

  • 时间复杂度:O(max⁡(max⁡(forbidden)+a,x)+b)。表达式为广度优先搜索的位置上限,每个位置最多会被计算两次。
  • 空间复杂度:O(max⁡(max⁡(forbidden)+a,x)+b)。表达式为广度优先搜索的位置上限,是队列和哈希集合的空间复杂度。
    author:力扣官方题解

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/949359.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

BRAM资源不够用?不怕!这里有FPGA BRAM省资源小秘招!

FPGA的BRAM和LUT等资源都是有限的&#xff0c;在FPGA开发过程中&#xff0c;可能经常遇到BRAM或者LUT资源不够用的情况。 一般建议BRAM和LUT资源的消耗不要超过80%&#xff0c;当然高端一点的FPGA芯片也可以放宽到90%&#xff0c;超过这个限制&#xff0c;可能就会出现时序违例…

必看!银行业软件测试岗位需求暴增的原因解密!

根据2023年3月中共中央、国务院印发《党和国家机构改革方案》&#xff0c;要求统筹推进中国人民银行分支机构改革。包括&#xff1a;撤销中国人民银行大区分行及分行营业管理部、总行直属营业管理部和省会城市中心支行&#xff0c;在31个省&#xff08;自治区、直辖市&#xff…

【解决】提示“找不到该项目的文件或文件夹如何删除”办法

在删除一些文件或文件夹中出现操作错误&#xff0c;后面想删除文件或文件夹时&#xff0c;弹出以下的状态栏提示↓↓↓ 此时无论怎么重启计算机还是快捷键都删除不了。 那么可采取下面的方法&#xff1a; ① 在该文件或文件夹内新建记事本&#xff0c;在记事本中输入以下代码…

QT设置mainwindow的窗口title

QT设置mainwindow的窗口title 在QT程序中&#xff0c;通常会有**aaaa-[bbbbbbb]**这种形式的title&#xff0c;对于刚上手qt的程序员同学&#xff0c;可能会简单的以为修改这种title&#xff0c;就是使用setWindowTitle这个接口&#xff0c;其实只对了一半&#xff0c;这种形式…

SpringBoot 使用 EMQX

一、SpringBoot服务器端 1. 在centos搭建 EMQX服务 2. 创建API密码 3. 在SpringBoot 的yml中添加mqqt的配置 #配置 emqx:ip: 47.109.49.176port: 18083api: xxxxxxxx &#xff08;自己的api&#xff09;secret: xxxxxxxxx &#xff08;自己的secret&#xff09; 4. 因为…

GPT4不限使用、内容加密更安全,ChatGPT企业版能否成为公司必备工具?

近日&#xff0c;OpenAI推出了ChatGPT企业版&#xff0c;这款AI助手为企业提供了更快速度的不限次数使用的GPT4。它还包括可以扩展上下文窗口以处理更长文本、加密、企业级安全与隐私保护&#xff0c;以及账户组管理功能。 在ChatGPT成功推出9个月后&#xff0c;这款流行的聊…

x86 汇编手册快速入门

本文翻译自&#xff1a;Guide to x86 Assembly 在阅读 Linux 源码之前&#xff0c;我们需要有一些 x86 汇编知识。本指南描述了 32 位 x86 汇编语言编程的基础知识&#xff0c;包括寄存器结构&#xff0c;数据表示&#xff0c;基本的操作指令&#xff08;包括数据传送指令、逻…

42、Flink 的table api与sql之Hive Catalog

Flink 系列文章 1、Flink 部署、概念介绍、source、transformation、sink使用示例、四大基石介绍和示例等系列综合文章链接 13、Flink 的table api与sql的基本概念、通用api介绍及入门示例 14、Flink 的table api与sql之数据类型: 内置数据类型以及它们的属性 15、Flink 的ta…

匠心新品:大彩科技超薄7寸WIFI线控器发布,热泵、温控器、智能家电首选!

一、产品介绍 此次发布一款7寸高清全新外壳产品&#xff0c;让HMI人机界面家族再添一新成员。该产品相比其他外壳有以下5个大改动&#xff1a; 1 表面玻璃盖板使用2.5D立体结构&#xff1b; 2 液晶盖板采用一体黑设计&#xff0c;且液晶屏与触摸板是全贴合结构&#xff1b; …

leetcode 84. 柱状图中最大的矩形

2023.8.30 本题和接雨水 有点类似&#xff0c;依旧用双指针来解。但是本题要记录的是当前柱子 左右两侧第一个小于该柱子的索引。将其保存在两个数组中&#xff0c;最后再求最大面积。代码如下&#xff1a; class Solution { public:int largestRectangleArea(vector<int&g…

图表背后的故事:数据可视化的威力与影响

数据可视化现在在市场上重不重要&#xff1f;这已经不再是一个简单的问题&#xff0c;而是一个不可忽视的现实。随着信息时代的来临&#xff0c;数据已经成为企业和组织的核心资产&#xff0c;而数据可视化则成为释放数据价值的重要工具。 在当今竞争激烈的商业环境中&#xf…

css换行

强制显示一行&#xff0c;超出... .box{white-space: nowrap; /* 强制显示一行 */overflow: hidden;text-overflow: ellipsis; /* 超出... */ } 自动换行 一般默认制动换行 .box1{word-wrap:break-word; } 显示2行&#xff0c;超出... .box2 {overflow: hidden;display: -…

SpringBoot初级开发--服务请求(GET/POST)所有参数的记录管理(8)

服务端在定位错误的时候&#xff0c;有时候要还原现场&#xff0c;这就要把当时的所有入参参数都能记录下来&#xff0c;GET还好说&#xff0c;基本NGINX都会记录。但是POST的请求参数基本不会被记录&#xff0c;这就需要我们通过一些小技巧来记录这些参数&#xff0c;放入日志…

Mybatis1.5 单个条件(动态SQL)

1.5 单个条件&#xff08;动态SQL&#xff09; 1.5.1 编写接口方法1.5.2 编写SQL语句1.5.3 编写测试方法 如上图所示&#xff0c;在查询时只能选择 品牌名称、当前状态、企业名称 这三个条件中的一个&#xff0c;但是用户到底选择哪儿一个&#xff0c;我们并不能确定。这种就属…

【小吉测评】哔哩哔哩接入AI?!效果如何?

文章目录 &#x1f384;前言⭐申请方式&#x1f3f3;️‍&#x1f308;注意 &#x1f6f8;简介&#x1f354;上手体验&#x1f6f8;进行数学计算&#x1f970;可以写代码吗 &#x1f384;前言 最近人工智能特别火&#xff0c;chatgpt&#xff0c;Claude2&#xff0c;文心一言等…

怎么把图片放大并且清晰?有详细的方法步骤

怎么把图片放大并且清晰&#xff1f;数字图像处理中的图片放大是许多行业和领域中广泛应用的一项技术。常规的放大方法通过插值或复制像素的方式增加像素数&#xff0c;但这会导致失真和模糊。无损放大是一种特殊的放大方法&#xff0c;它可以通过数学算法来增加图片的尺寸&…

从Gamma空间改为Linear空间会导致性能下降吗

1&#xff09;从Gamma空间改为Linear空间会导致性能下降吗 2&#xff09;如何处理没有使用Unity Ads却收到了GooglePlay平台的警告 3&#xff09;C#端如何处理xLua在执行DoString时候死循环 4&#xff09;Texture2DArray相关 这是第350篇UWA技术知识分享的推送&#xff0c;精选…

Transformer升级之路:逆用Leaky ReRoPE

©PaperWeekly 原创 作者 | 苏剑林 单位 | 科学空间 研究方向 | NLP、神经网络 在《Transformer升级之路&#xff1a;无限外推的ReRoPE&#xff1f;》中&#xff0c;笔者提出了 ReRoPE 和 Leaky ReRoPE&#xff0c;诸多实验结果表明&#xff0c;它们能够在几乎不损失训练效…

【设计模式】装饰者模式

目录 一、定义二、结构三、优点四、使用场景五、代码示例六、截图示例 一、定义 1.在不改变现有对象结构的情况下&#xff0c;动态给该对象添加额外功能的模式 2.类B继承于类A&#xff0c;并将类A作为B类的属性&#xff08;B类聚合A类&#xff09; 3.BufferedInputStream、Buff…

单元测试用例mock的使用方法

单元测试用例mock的使用方法 提升代码测试覆盖率的关键策略 为什么单元测试是如此重要&#xff1f; 在软件开发中&#xff0c;单元测试是一个关键的环节&#xff0c;可以确保代码的质量和稳定性。而在进行单元测试时&#xff0c;使用mock对象可以帮助我们更好地测试代码逻辑…