leetcode 21天动态规划入门——从0到0.5【Day04】小兔子的跳跃游戏
写在前面
到了动态规划的第四天啦~今天可是不寻常的一天,因为今天的题的动态转化方程虽然很简单就能推导出来,但是今天的两个动态转化方程都可以进行略微的转换,使得时间复杂度降低,话不多说,冲!!!
题目
题目一
- 跳跃游戏 难度系数:***
给定一个非负整数数组 nums ,你最初位于数组的 第一个下标 。
数组中的每个元素代表你在该位置可以跳跃的最大长度。
判断你是否能够到达最后一个下标。
示例
示例1:
输入:nums = [2,3,1,1,4]
输出:true
解释:可以先跳 1 步,
从下标 0 到达下标 1,
然后再从下标 1 跳 3 步到达最后一个下标。
示例2:
输入:nums = [3,2,1,0,4]
输出:false
解释:无论怎样,总会到达下标为 3 的位置。
但该下标的最大跳跃长度是 0 ,
所以永远不可能到达最后一个下标。
提示
1 <= nums.length < 3*10^4
0 <= nums[i] <= 10^5
思路
动态规划老样子
模拟 一部分推出全局
需要维护一个临时数组dp来记录当前小兔子能跳到的最远距离。
根据示例就可以推算出动态转化方程:
当前能跳跃的最远距离是:
- 当前位置+当前位置能跳的距离
- 前一个位置能到达的最远距离
二者之间进行取最大值即当前能跳跃的最大距离。
dp[i] = Math.max(dp[i-1],nums[i]+i);
代码实现
class Solution {
public boolean canJump(int[] nums) {
//特殊情况特殊处理
if(nums.length < 2) return true;
//维护一个dp数组
int dp[] = new int [nums.length+1];
//最远能跳多远
int maxStep = nums[0];
dp[0] = nums[0];
//模拟推导
for (int i = 1 ; i< nums.length ;i++){
//如果当前位置大于了能跳的最远位置直接返回false
if (i > maxStep) return false;
//动态转化方程
dp[i] = Math.max(dp[i-1],nums[i]+i);
//维护最远位置
maxStep = Math.max(dp[i],maxStep);
if(maxStep > nums.length) return true;
}
return true;
}
}
执行结果
代码优化
想一想为什么会导致时间复杂度这么高呢?
因为你既维护了一个dp数组的同时又维护了一个最远步长
每次都需要经历O(n)次遍历 比较 故时间 消耗会更大
那么我们只维护一个最远步长 能否实现呢?
优化如下
class Solution {
public boolean canJump(int[] nums) {
//维护一个能到达最远位置的变量替换dp数组
int maxIndex = nums[0];
for (int i = 0 ; i< nums.length ;i++){
if(i > maxIndex )return false;
maxIndex = Math.max(i+nums[i],maxIndex);
if (maxIndex > nums.length)return true;
}
return true;
}
}
执行结果
典型的用空间换时间的做法qwq
题目二
- 跳跃游戏 II 难度系数:****
给你一个非负整数数组 nums ,你最初位于数组的第一个位置。
数组中的每个元素代表你在该位置可以跳跃的最大长度。
你的目标是使用最少的跳跃次数到达数组的最后一个位置。
假设你总是可以到达数组的最后一个位置。
示例
示例1:
输入: nums = [2,3,1,1,4]
输出: 2
解释: 跳到最后一个位置的最小跳跃数是 2。
从下标为 0 跳到下标为 1 的位置,跳 1 步,
然后跳 3 步到达数组的最后一个位置。
示例2:
输入: nums = [2,3,0,1,4]
输出: 2
提示
1 <= nums.length <= 10^4
0 <= nums[i] <= 1000
思路
维护一个dp[]数组用来表示到达第i个位置时所需要的最小步数。
思路在哪里?
思路在于 到达最后一个点之前有很多点都能够一步到达这个点
这就是求解动态转化方程的思路:
用一个指针遍历dp找到所有的位置能到的最小步数,用另一个指针来遍历dp获取得到离最后一个点最远的能够一次到达最后一个点的指针位置。
获得动态转化方程:
dp[i] = dp[j] + 1
代码实现
class Solution {
public int jump(int[] nums) {
if (nums.length<2)return 0;
int[] dp = new int[nums.length];
dp[0] = 0;
//i是代表当前能跳跃的最远位置
for (int i = 1, j = 0; i < nums.length; i++) {
//如果当前能到达的位置<最远需要到达的位置j指针后移
while (j + nums[j] < i) j++;
//动态转化方程 进行dp[i]赋值
dp[i] = dp[j] + 1;
}
//维护的数组返回的是到达位置n需要的最小步数
return dp[nums.length - 1];
}
}
执行结果
双百! 这是来自宫水三叶小姐姐的思路(DP+贪心+双指针)的巧妙结合
学会了没? 学费了没?
写在最后
DAY4的动态规划是否让你感觉到不一样的难度?
今天不只是简单的寻找动态转化方程,
而更多地在于如何根据方程来优化维护的数组。
用更巧妙地方法,解决复杂的问题
加油 ~坚持打卡第四天!
最后
每天进步点 每天收获点
愿诸君 事业有成 学有所获
如果觉得不错 别忘啦一键三连哦~