LeetCode每日一题 1654. 到家的最少跳跃次数

题目描述

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

跳蚤跳跃的规则如下:

  • 它可以往前跳恰好 a 个位置(即往右跳)。
  • 它可以往后跳恰好 b 个位置(即往左跳)。
  • 它不能连续往后跳 2 次。
  • 它不能跳到任何 forbidden 数组中的位置。
  • 跳蚤可以往前跳超过它的家的位置,但是它不能跳到负整数的位置。

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

解题思路

为了解决这个问题,我们可以使用广度优先搜索(BFS)算法来遍历所有可能的跳跃路径,以找到最短的路径。

我们从位置 0 出发,记录当前位置、跳跃次数以及是否连续向后跳。在每一步跳跃中,我们考虑向前跳跃和向后跳跃,分别计算下一个位置,然后检查是否满足跳跃规则。如果满足规则且未访问过,我们将该状态加入队列,继续进行搜索。

在搜索过程中,我们需要维护一个哈希集合来记录已经访问过的状态,以避免重复计算。另外,我们需要限制搜索的范围,避免生成过多无效的状态。

具体算法步骤如下:

初始化队列,将初始状态 (0, 0, false) 加入队列,表示跳蚤起始位置在 0,跳跃次数为 0,且未连续向后跳跃。
初始化哈希集合,用于记录已访问的状态。
初始化最大位置 maxPos,此为本题难点,已有大佬证明为max(x, max(forbidden)+ a + b) 。核心证明思路是在跳蚤出界后交换路径的等价性。参见: 到家的最少跳跃次数(最短路+证明)
开始 BFS 循环:
从队列中取出一个状态 (pos, steps, isBackward)。
若当前位置为目标位置 x,返回跳跃次数 steps。
向前跳跃:计算下一个位置 nextPos = pos + a,若未越界、不在 forbidden 中且未访问过,则将该状态加入队列并标记为已访问。
向后跳跃:若允许向后跳且未连续向后跳跃,计算下一个位置 nextPos = pos - b,若未越界、不在 forbidden 中且未访问过,则将该状态加入队列并标记为已访问。
若循环结束后未找到有效路径,返回 -1

代码实现

class Solution {
public:
    int minimumJumps(vector<int>& forbidden, int a, int b, int x) {
        unordered_set<int> forbiddenSet(forbidden.begin(), forbidden.end());
        unordered_set<long long> visited; // 记录已访问状态,用 position * 10001 + isBackward 表示

        queue<tuple<int, int, bool>> q; // (位置,步数,是否连续向后跳)
        q.push({0, 0, false});
        visited.insert(0);

        int maxPos = max(x, *max_element(forbidden.begin(), forbidden.end()) + a + b) ;
        
        while (!q.empty()) {
            auto [pos, steps, isBackward] = q.front();
            q.pop();

            if (pos == x) {
                return steps;
            }

            // 向前跳跃
            int nextPos = pos + a;
            if (nextPos <= maxPos && !forbiddenSet.count(nextPos) && visited.find(static_cast<long long>(nextPos) * 10001) == visited.end() && nextPos >= 0) {
                visited.insert(static_cast<long long>(nextPos) * 10001);
                q.push({nextPos, steps + 1, false});
            }

            // 向后跳跃(如果允许且未连续向后跳)
            if (!isBackward) {
                nextPos = pos - b;
                if (nextPos >= 0 && !forbiddenSet.count(nextPos) && visited.find(static_cast<long long>(nextPos) * 10001 + 1) == visited.end()) {
                    visited.insert(static_cast<long long>(nextPos) * 10001 + 1);
                    q.push({nextPos, steps + 1, true});
                }
            }
        }

        return -1; // 未找到有效路径
    }
};

复杂度分析

时间复杂度:每个状态最多访问一次,因此时间复杂度为 O(N),其中 N 为位置的最大范围。
空间复杂度:需要使用队列和哈希集合来记录状态,因此空间复杂度为 O(N)。

你可能感兴趣的:(LeetCode每日一题,leetcode,算法,职场和发展)