一.跳跃游戏简单介绍
1. 跳跃游戏简单介绍
跳跃游戏是一种典型的算法题目,经常是给定一数组arr,从数组的某一位置i出发,根据一定的跳跃规则,比如从i位置能跳arr[i]步,或者小于arr[i]步,或者固定步数,直到到达某一位置,可能是数组的最后一个位置,也有可能是某一特别的数值处。
在上一期有可以用用贪心、动态规划、dfs解决的题目,这一期主要关注在跳跃游戏中就经常使用的动态规划剪枝、前缀和、滑动窗口和BFS,由于在大部分情况下,能用DFS解决的题目都可以用BFS解决,且两种方法有基本相同的复杂度,尤其是在跳跃游戏这类题目中,可以视为一种方法。
2. 经典问题回溯:青蛙跳台 与 爬楼梯最小成本
青蛙跳台与爬楼梯是跳跃游戏大类中最为经典的题目,其解题思路也是跳跃游戏中最为核心的解决问题的方法。
(1)leetcode70 爬楼梯
假设你正在爬楼梯。需要 n
阶你才能到达楼顶。
每次你可以爬 1或 2个台阶。你有多少种不同的方法可以爬到楼顶呢?
输入:n = 2
输出:2
解释:有两种方法可以爬到楼顶。
1. 1 阶 + 1 阶
2. 2 阶
动态规划
class Solution {
public int climbStairs(int n) {
if(n <= 1) return n;
int[] dp = new int[n+1];
dp[1] = 1;
dp[2] = 2;
for(int i = 3; i <= n; i++){
dp[i] = dp[i-1] + dp[i-2];
}
return dp[n];
}
}
解析:当我们来到第i个楼梯,我们有可能从哪里上来的?那么答案只有两个,从i-1处蹦一格,从i-2处蹦两格,以f(i)代表在当前格存储的方法数目,即:
我们可以得到动态规划的递推公式:
注意边界条件,在i <= 2时,我们直接进行返回就好。
总结
动态规划的初始条件: dp[1] = 1,dp[2] = 2,由题目简单可得
动态规划的递推公式:f(i) = f(i-1) + f(i-2)
动态规划的终止条件:来到数组最后一个位置即可
滚动数组优化
class Solution {
public int climbStairs(int n) {
if(n <= 2) return n;
int num1 = 1;
int num2 = 2;
int num3 = 3;
for(int i = 3; i <= n; i++){
num3 = num1 + num2;
num1 = num2;
num2 = num3;
}
return num3;
}
}
从上述动态规划的做法来看,我们可以节省空间,进行滚动数组的优化,因为f(i)的值只与f(i-1)与f(i-2)有关,即与当前需求变量不相关变量可以用完就扔。
用num1与num2进行替代f(i-1)与f(i-2),随着遍历交替前进。最后在num3中保存的就是最终需要的值。
(2)leetcode剑指 Offer 10- II. 青蛙跳台阶问题
一只青蛙一次可以跳上1级台阶,也可以跳上2级台阶。求该青蛙跳上一个 n 级的台阶总共有多少种跳法。
答案需要取模 1e9+7(1000000007),如计算初始结果为:1000000008,请返回 1。
输入:n = 2
输出:2
输入:n = 7
输出:21
本题与leetcode70 爬楼梯是同一个问题,但是由于整数取值的范围较大,导致益出,需要取余,在循环中进行取余即可,用滚动数组的方法比较方便。
class Solution {
public int numWays(int n) {
if(n <= 1) return 1;
int num1 = 1;
int num2 = 1;
int num3 = 2;
for(int i = 2; i <= n; i++){
num3 = (num1 + num2) % 1000000007;
num1 = num2;
num2 = num3;
}
return num3;
}
}
(3)剑指 Offer II 088. 爬楼梯的最少成本
数组的每个下标作为一个阶梯,第 i 个阶梯对应着一个非负数的体力花费值 cost[i](下标从 0 开始)。
每当爬上一个阶梯都要花费对应的体力值,一旦支付了相应的体力值,就可以选择向上爬一个阶梯或者爬两个阶梯。
请找出达到楼层顶部的最低花费。在开始时,你可以选择从下标为 0 或 1 的元素作为初始阶梯。
输入:cost = [10, 15, 20]
输出:15
解释:最低花费是从 cost[1] 开始,然后走两步即可到阶梯顶,一共花费 15 。
此题和 (1)和(2)是同一个类型的题目,但是需要考虑成本的问题,即在动态规划的递推公式中加上成本项。
class Solution {
public int minCostClimbingStairs(int[] cost) {
int[] dp = new int[cost.length+1];
dp[0] = 0;
dp[1] = 0;
for(int i = 2; i <= cost.length; i++){
dp[i] = Math.min(dp[i-1]+cost[i-1],dp[i-2]+cost[i-2]);
}
return dp[cost.length];
}
}
注意,此时到达数组顶部对应的是数组最后一个位置向后一个位置,即
总结
动态规划的初始条件: dp[0] = 0,dp[1] = 0, 反直觉的都是0,因为可以选择直接站在上面
动态规划的递推公式:f(i) = min(f(i-1)+nums[i-1] + f(i-2)&#