目录
1654. 到家的最少跳跃次数
题目描述:
有一只跳蚤的家在数轴上的位置 x
处。请你帮助它从位置 0
出发,到达它的家。
跳蚤跳跃的规则如下:
- 它可以 往前 跳恰好
a
个位置(即往右跳)。 - 它可以 往后 跳恰好
b
个位置(即往左跳)。 - 它不能 连续 往后跳
2
次。 - 它不能跳到任何
forbidden
数组中的位置。
跳蚤可以往前跳 超过 它的家的位置,但是它 不能跳到负整数 的位置。
给你一个整数数组 forbidden
,其中 forbidden[i]
是跳蚤不能跳到的位置,同时给你整数 a
, b
和 x
,请你返回跳蚤到家的最少跳跃次数。如果没有恰好到达 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
中。
实现代码与解析:
bfs
class Solution {
public:
int minimumJumps(vector<int>& forbidden, int a, int b, int x) {
unordered_set<int> f; // 记录禁止的位置,和前进访问过的位置,防止死循环
for (auto &t: forbidden) f.insert(t);
queue<tuple<int, int, bool>> q; // 当前位置,当前步数,到此位置是前进还是后退而来 true 前进 false 后退
q.push({0, 0, false}); // 初始化,起点 不能后退为负数,当作false处理
// bfs
while (q.size())
{
auto [j, k, l] = q.front();
q.pop(); // 出队
if (j == x) return k; // 到家的位置,返回步数
// 向前,位置合法 且 不超过范围
if (!f.count(j + a) && j + a <= 6000)
{
q.push({j + a, k + 1, true}); //入队
f.insert(j + a); // 标记位置
}
// 向后,位置合法 且 不超过范围,并且不能连续两次向后
if (l && !f.count(j - b) && j - b >= 0)
{
q.push({j - b, k + 1, false}); //入队
}
}
return -1;
}
};
原理思路:
一开始很容易觉得是dp来写,其实是bfs,dfs只能求一个符合条件的结果,而bfs是求最短。
其实注释写的已经很清楚了,可以直接看注释。
队列记录:当前位置,当前步数,到此位置是前进还是后退而来。
bfs首先写得到结果的条件,就是到家了 j == x。
前进时,判断要到的位置是否合法,且不超过上界,上界可以看力扣官解的证明,太麻烦了,试一个相对较大符合条件的数就行。入队并且标记位置,后退时不能到已经走过的位置,因为只能退一次,要是到已经走过的位置,只能往前,不就重复了么。
后退时,判断大于等于0,并且位置合法即可。
若没有符合条件的结果,返回 -1。
感觉以后可以改用emplace,很多人都这样写,代替push和insert确实方便,而且还快。