跳跃游戏 (动态规划剪枝/前缀和/滑动窗口/BFS剪枝)

一.跳跃游戏简单介绍

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)代表在当前格存储的方法数目,即:

 我们可以得到动态规划的递推公式:

f(i) = f(i-1)+f(i-2)

注意边界条件,在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)&#
  • 8
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值