动态规划解决跳台阶问题记录一下

15 篇文章 0 订阅

动态规划解决跳台阶问题

前言
  • 动态规划是一种把原问题分解为相对简单的一系列子问题的方式进行求解的方法。动态规划通常可以用于解决有重叠子问题和最优子结构的问题。
  • 在LeetCode上有非常多关于动态规划的问题,很有逻辑性和技巧性,这次以一道很经典的跳台阶问题来学习动态规划。

过程

leetCode原题:一只青蛙一次可以跳一个台阶,也可以跳两个台阶,求该青蛙跳上10级台阶共有多少种跳法。

  • 首先当我们解析一下其过程,就会发现这题不难:

    • 当有一级台阶时,只有一种跳台阶的方式。
    • 当有两级台阶时,可以选择一级一级跳台阶,也可以一次性跳两级台阶,就有两种跳台阶方式。
    • 当有三级台阶时,需要先跳到一级台阶再跳两个台阶,也可以跳到二级台阶再跳一个台阶,相当于f(1)+f(2)方式。
    • 当有四级台阶时,需要先跳到二级台阶再跳两个台阶,也可以跳到三级台阶再跳一个台阶,相当于f(2)+f(3)方式。
    • 当有十级台阶时,需要先跳到8级台阶再跳两个台阶,也可以跳到9级台阶再跳一个台阶,相当于f(8)+f(9)方式。
  • 上面的过程就是动态规划的穷举分析法。

  • 然后我们可以发现当只有一级或者两级台阶时,跳的方式是可以确定的,就只有一种和两种。这就是动态规划的确定边界

  • 后面的不管是3级台阶还是10级台阶,其跳台阶方式的都可以用f(x) = f(x-1)+f(x-2)方式进行计算得出结果。找到规律,确定其最优子结构函数。

  • 那么f(x) = f(x-1)+f(x-2)就是动态规划的状态转移方式。

  • 最后我们就用代码实现:

/**
 * 动态规划:
 *
 * @AUTHOR ZRH
 * @DATE 2021/4/22
 */
public class Test1 {

    public static void main(String[] args) {
    	// 只有一级或两级台阶就直接返回结果
        if (1 == y) {
            return 1;
        }
        if (2 == y) {
            return 2;
        }
        // a和b为当前台阶的前两次台阶计算结果
        int a = 1, b = 2, result = 0;
        for(int i = 3; i <= y; i++){
            result = a + b;
            a = b;
            b = result;
        }
        return result;
    }
}
  • 这里是由一级,二级,,,十级台阶计算方式,是由自底向上的方式进行计算。其时间复杂度是O(n)。
  • 这道题除了动态规划以外,还可以用递归的方式进行解决,只是时间复杂度是O(2^n)。但也可以优化为O(n)。
/**
 * 动态规划:
 *
 * @AUTHOR ZRH
 * @DATE 2021/4/22
 */
public class Test1 {

    public static void main(String[] args) {
        Integer num = 10;
        System.out.println("test1 : " + test1(num));
    }

    /**
     * 暴力递归解决
     */
    private static Integer test1(Integer y) {
        if (1 == y) {
            return 1;
        }
        if (2 == y) {
            return 2;
        }
        return test1(y - 1) + test1(y - 2);
    }
}
  • 上面这种方式是自顶向下的方式进行计算,每增加一个台阶就相当于增加一个f(x)=f(x-1)+f(x-2)计算方法,所以时间复杂度是O(2^n)。
  • 针对上面这种情况,可以发现整个递归过程有大量的重复计算,如果把计算结果进行保存下来就可以减少其重复计算的时间消耗。
/**
 * 动态规划:
 *
 * @AUTHOR ZRH
 * @DATE 2021/4/22
 */
public class Test1 {

    public static void main(String[] args) {
        Integer num = 10;
        System.out.println("test2 : " + test2(num));
    }

    /**
     * 优化递归方式
     */
    private final static Map<Integer, Integer> map = new HashMap<>();
    private static Integer test2(Integer y) {
        if (1 == y) {
            return 1;
        }
        if (2 == y) {
            return 2;
        }
        if(map.containsKey(y)){
            return map.get(y);
        }
        int value = test1(y - 1) + test1(y - 2);
        map.put(y, value);
        return value;
    }
}
  • 上述代码使用了map缓存了每次计算的结果,减少了大量重复计算的消耗,其时间复杂度是O(n)。

最后

  • 动态规划的解题思路:
    • 穷举分析
    • 确定边界
    • 最优子结果
    • 状态转移方程
    • 代码实现
  • 针对上述过程,动态规划有个可参考的框架:
dp[0][0][...] = 边界值
for(状态1 :所有状态1的值){
    for(状态2 :所有状态2的值){
        for(...){
          //状态转移方程
          dp[状态1][状态2][...] = 求最值
        }
    }
}
  • 如果有什么地方写的不对的,欢迎指出,我确认后会加以改正 -_-
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值