地址:https://leetcode.com/problems/jump-game/
题目:
Given an array of non-negative integers, you are initially positioned at the first index of the array.
Each element in the array represents your maximum jump length at that position.
Determine if you are able to reach the last index.
Example 1:
Example 2:
理解:
这道题应该用dp做吧。。solution里总结了一下dp的步骤。
- Start with the recursive backtracking solution
- Optimize by using a memoization table (top-down dynamic programming)
- Remove the need for recursion (bottom-up dynamic programming)
- Apply final tricks to reduce the time / memory complexity
backtracking:
Start with the recursive backtracking solution
简单粗暴容易理解。
注意furthest的地方取的是min,为了保证不越界
class Solution {
public:
bool canJump(vector<int>& nums) {
return canJumpFromPos(0, nums);
}
bool canJumpFromPos(int pos, vector<int>& nums) {
if (pos == nums.size() - 1)
return true;
int furthest = min(pos + nums[pos], static_cast<int>(nums.size() - 1));
for (int i = pos + 1; i <= furthest; ++i) {
if (canJumpFromPos(i, nums))
return true;
}
return false;
}
};
一个简单的优化就是,从furthest开始倒着判断
Intuitively, this means we always try to make the biggest jump such that we reach the end as soon as possible
for (int i = pos + 1; i <= furthest; ++i)
for (int i = furthest; i > pos; --i)
Dynamic Programming Top-down:
把上面的backtracking的实现稍稍改一下
Optimize by using a memoization table (top-down dynamic programming)
enum getType:int {
GOOD, BAD, UNKNOWN
};
class Solution {
public:
bool canJump(vector<int>& nums) {
vector<getType> memo(nums.size(), UNKNOWN);
memo[memo.size() - 1] = GOOD;
return canJumpFromPos(0, nums, memo);
}
bool canJumpFromPos(int pos, vector<int>& nums,vector<getType>& reachable) {
if (reachable[pos] != UNKNOWN) {
return reachable[pos] == GOOD ? true : false;
}
int furthest = min(pos + nums[pos], static_cast<int>(nums.size() - 1));
for (int i = furthest; i > pos; --i) {
if (canJumpFromPos(i, nums, reachable)) {
reachable[pos] == GOOD;
return true;
}
}
reachable[pos] == BAD;
return false;
}
};
Dynamic Programming Bottom-up
Remove the need for recursion (bottom-up dynamic programming)
由于backtracking和top-down的思路都是从开头开始,然后向后寻找,如果后面是good,则把当前位置也改为good。因此,我们可以从后往前直接求memo而避免递归。
enum getType:int {
GOOD, BAD, UNKNOWN
};
class Solution {
public:
bool canJump(vector<int>& nums) {
vector<getType> memo(nums.size(), UNKNOWN);
memo[memo.size() - 1] = GOOD;
for (int i = nums.size() - 2; i >= 0; --i) {
int furthest = min(i + nums[i], static_cast<int>(nums.size() - 1));
for (int j = furthest; j > i; --j) {
if (memo[j] == GOOD) {
memo[i] = GOOD;
break;
}
}
}
return memo[0] == GOOD;
}
};
Greedy
上面的bottom-up算法里,我用了从后向前的方法。事实上,可以用从前向后的方法
for (int i = pos + 1; i <= furthest; ++i)
这样,判断完最靠左的good就break了,后面的其实是没有用到的。借助这个特点,我们只需保存最靠左good的位置就好了。
class Solution {
public:
bool canJump(vector<int>& nums) {
int leftMost = nums.size() - 1;
for (int i = nums.size() - 2; i >= 0; --i) {
if (i + nums[i] >= leftMost) {
leftMost = i;
}
}
return leftMost == 0;
}
};
优雅、简洁,而且代码还短