题目描述
给定一只跳蚤在数轴上的位置 x
处。帮助它从位置 0 出发,到达它的家。
跳蚤跳跃的规则如下:
- 它可以往前跳恰好
a
个位置(即往右跳)。 - 它可以往后跳恰好
b
个位置(即往左跳)。 - 它不能连续往后跳 2 次。
- 它不能跳到任何 forbidden 数组中的位置。
- 跳蚤可以往前跳超过它的家的位置,但是它不能跳到负整数的位置。
给定一个整数数组 forbidden
,其中 forbidden[i]
是跳蚤不能跳到的位置,同时给你整数 a
、b
和 x
,请你返回跳蚤到家的最少跳跃次数。如果没有恰好到达 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)。