class Solution {
public:
int jump(vector<int>& nums) {
}
};
从数组的第 0 个位置开始跳,跳的距离小于等于数组上对应的数。求出跳到最后个位置需要的最短步数。
int jump(std::vector<int> arr){
if(arr.empty()){
return 0;
}
int step = 0;
int cur = 0;
int next = 0;
for (int i = 0; i < arr.size(); ++i) {
if(cur < i){
step++;
cur = next;
}
next = std::max(next, i + arr[i]);
}
return step;
}
思路:别想那么多,就挨着跳吧
class Solution {
public:
int jump(vector<int>& nums) {
int ans = 0;
int begin = 0, end = 0; //[begin, end]
while (end < nums.size() - 1){ //不需要检查最后一个位置是因为,最后一个位置我们不用跳了已经
// 能跳到最远的距离
int temp = 0;
for (int i = begin; i <= end; ++i) {
temp = std::max(nums[i] + i, temp);
}
begin = end + 1; // 下一次起跳点范围开始的格子
end = temp; // 下一次[起跳点]范围结束的格子
++ans; // 跳跃次数
}
return ans;
}
};
思路:顺藤摸瓜
我们每次在可跳范围内选择可以使得跳的更远的位置(为了求最小步数,我们只有当不得不跳时,才跳。)
如下图,开始的位置是 2,可跳的范围是橙色的。然后因为 3 可以跳的更远,所以跳到 3 的位置
如下图,然后现在的位置就是 3 了,能跳的范围是橙色的,然后因为 4 可以跳的更远,所以下次跳到 4 的位置。
思路:
class Solution {
public:
int jump(vector<int>& nums) {
if(nums.size() == 1){
return 0;
}
int jump = 0; // 记录走的最大步数
int curDistance = 0; //以当前跳跃步数,能到的最远位置,比如: jump=1跳一次时,最远能到下标currJumpMax=2
int nextDistance = 0; //当前位置能到的最远位置
for (int i = 0; i < nums.size(); ++i) {
nextDistance = max(nums[i] + i, nextDistance); //找能跳的最远的
if (i == curDistance){//已经走到了当前跳跃步数的边界
if(curDistance != nums.size() - 1){ //还没有到达终点
jump++;//我们不得不再跳一次
curDistance = nextDistance; //并记录当前跳跃步数能到的最远位置
if (nextDistance >= nums.size() - 1) break; // 下一步的覆盖范围已经可以达到终点,结束循环
}else{
break; // 已经到达终点,不用做ans++操作了,直接结束
}
}
}
return jump;
}
};
思路: 贪心策略,通过反向查找出发位置
我们的目标是到达数组的最后一个位置,因此我们可以考虑最后一步跳跃前所在的位置,该位置通过跳跃能够到达最后一个位置。
如果有多个位置通过跳跃都能够到达最后一个位置,那么我们应该如何进行选择呢?直观上看,我们可以「贪心」的选择距离最后一个位置最远的那个位置,也就是对应下标最小的那个位置。因此,我们可以从左到右遍历数组,选择第一个满足要求的位置。
找到最后一步跳跃前所在的位置后,我们继续贪心的寻找倒数第二步跳跃所在的位置,以此类推,直到找到数组的开始位置。
class Solution {
public:
int jump(vector<int>& nums) {
int pos = nums.size() - 1; //要找的位置
int step = 0;
while (pos != 0){ //是否到了第 0 个位置
for (int i = 0; i < pos; ++i) {
if(nums[i] >= pos - i){
pos = i; //更新要找的位置
step++;
break;
}
}
}
return step;
}
};
(1)定义状态
(2)转移方程
(3)初始化
(4)返回值
class Solution {
public:
int jump(vector<int>& nums) {
int n = nums.size();
std::vector<int> dp(n);
dp[0] = 0;
for (int i = 1; i < n; ++i) {
for (int j = 0; j < i; ++j) {
if(j + nums[j] >= i){
dp[i] = min(dp[i], 1 + dp[j]);
}
}
}
return dp[n - 1];
}
};
(5)怎么优化呢?
class Solution {
public:
int jump(vector<int>& nums) {
int n = nums.size();
std::vector<int> dp(n);
//i是代表当前能跳跃的最远位置
for (int i = 1, j = 0; i < n; ++i) {
//如果当前能到达的位置<最远需要到达的位置
while (j + nums[j] < i){
j++; //j指针后移
}
dp[i] = dp[j] + 1;
}
return dp[n - 1];
}
};
优先队列写法:
class Solution {
public:
int jump(vector<int>& nums) {
int n = nums.size();
vector<int> dp(n); //dp[i]: 到达i的最小跳跃次数
priority_queue<pair<int, int>> pq;
for (int i = 0; i < n; ++i) {
while (!pq.empty()) {
auto [d, j] = pq.top();
if (j + nums[j] < i) { //从j跳不到i及i后面的位置
pq.pop();
} else {
dp[i] = 1 - d;
break;
}
}
pq.push({-dp[i], i});
}
return dp[n - 1];
}
};
题目 | |
---|---|
leetcode:45. 能跳到终点的最少跳跃次数 II Jump Game II | |
leetcode:55. 能不能跳到末尾 Jump Game |