文章目录
- Tag
- 题目来源
- 题目解读
- 解题思路
- 实现细节
- 实现代码
- 复杂度分析
- 写在最后
Tag
【广搜】【上限证明】【图论】
题目来源
1654. 到家的最少跳跃次数.
题目解读
找到从位置 0
跳跃到位置 x
的最小跳跃次数,跳跃规则如下:
- 前进方向跳
a
个位置; - 后退方向跳
b
个位置; - 不能连续后退
2
次,也就是说,上一次是后退的,这次一定要前进;上次是前进的,这次可以前进也可以后退。 - 有一个禁止数组
forbidden
,表示禁止到达的位置。
如果没有合适的跳跃方案返回 -1
。
解题思路
本题求最小跳跃次数,是最短路问题。最短路问题一般都需要使用广度优先搜索,本题中的图是一个无限的图(因为有可能超过 x
位置后,还一直前进),因此需要限制搜索范围,否则无法处理无解的情况。
题目中已经给出了搜索的下限,不能跳到负整数位置,即下限 lower = 0
,关于上限的寻找与证明,参考的是 newhar 的解答。
a >= b
即一次的前进距离大于等于后退距离时,当到达的位置大于x + b
时,最多只能后退一次,也到不位置x
。这时不论怎么跳都不会到达位置x
了。此时的搜索上限是x + b
。a < b
即一次的前进距离小于后退距离时,上限为max(f + a +b, x)
具体证明请参考 newhar 的题解;- 最终的上限定为
upper = max(f + a + b, x + b)
。
找到了搜索的下限与上限之后,在该范围内进行广度优先搜索,当前位置到达 x
处,返回对应的步数;直到在范围内搜索无结果,最终返回 -1
。
实现细节
维护一个队列,存放三元组 pos
、dir
、step
,分别表示 当前的位置
、上一个是前进还是后退
(前进为 1
,后退为 -1
)、当前的步数
。
维护一个已经到达的位置和方向的集合,存放 位置*方向
。
将 forbidden
数组存入集合中,方便查找。
在进行 while
循环时,无论上次是前进还是后退的,这次都可以前进。上次是前进的话,还可以前进。
实现代码
class Solution {
public:
int minimumJumps(vector<int>& forbidden, int a, int b, int x) {
queue<tuple<int, int, int>> q; // 三元组(位置,方向,步数)
unordered_set<int> visited; // 已经到达的位置和方向 1表示前进 -1表示后退 存放位置*方向
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> forbSet(forbidden.begin(), forbidden.end());
while (!q.empty()) {
auto [pos, dir, step] = q.front();
q.pop();
// 到达位置,返回步数
if (pos == x) {
return step;
}
// 不论上一步是前进还是后退的,这一步都可以前进
int nextPos = pos + a;
int nextDir = 1;
if (lower <= nextPos && nextPos <= upper && !visited.count(nextPos * nextDir) && !forbSet.count(nextPos)) {
visited.emplace(nextPos * nextDir);
q.emplace(nextPos, nextDir, step + 1);
}
// 如果上一步是前进的,这一步还可以后退
if (dir == 1) {
nextPos = pos - b;
nextDir = -1;
if (lower <= nextPos && nextPos <= upper && !visited.count(nextPos * nextDir) && !forbSet.count(nextPos)) {
visited.emplace(nextPos * nextDir);
q.emplace(nextPos, nextDir, step + 1);
}
}
}
// 到达搜索上限后,仍然没有找到合适的跳跃方案,返回 -1
return -1;
}
};
复杂度分析
时间复杂度为搜索上限: O ( m a x ( m a x ( f o r b i d d e n ) + a , x ) + b ) O(max(max(forbidden) + a, x) + b) O(max(max(forbidden)+a,x)+b)。
空间复杂度为队列大小: O ( m a x ( m a x ( f o r b i d d e n ) + a , x ) + b ) O(max(max(forbidden) + a, x) + b) O(max(max(forbidden)+a,x)+b)。
写在最后
以上就是本篇文章的内容了,感谢您的阅读。🍗🍗🍗
如果感到有所收获的话可以给博主点一个 👍 哦。
如果文章内容有任何错误或者您对文章有任何疑问,欢迎私信博主或者在评论区指出。💬💬💬