代码随想录-刷题第三十九天

动态规划理论基础

动态规划的题目由重叠子问题构成,每一个状态一定是由上一个状态推导出来的。这一点就区分于贪心,贪心没有状态推导,而是从局部直接选最优的。

动态规划五步曲

  1. 确定dp数组(dp table)以及下标的含义
  2. 确定递推公式
  3. dp数组如何初始化
  4. 确定遍历顺序
  5. 举例推导dp数组

动态规划里面递推公式十分重要,但是确定dp数组,初始化,遍历顺序也同样十分重要,一定要严格按照这五步进行,将每一步的思路理清。

做动态规划的题目遇到问题时,最好的方式就是打印出dp数组,看是否和自己推理一致。


509. 斐波那契数

题目链接:509. 斐波那契数

思路:动态规划五步曲:

  1. dpi的定义为:第i个数的斐波那契数值是dpi

  2. 递推公式:dpi = dpi - 1 + dpi - 2

  3. 初始化:dp0 = 0, dp1 = 1

  4. 从递推公式可以看出,一定是从前向后遍历的。

  5. 举例看是否可行,当N为10的时候,dp数组应该是如下的数列:

    0 1 1 2 3 5 8 13 21 34 55

    如果代码写出来,发现结果不对,就把dp数组打印出来看看与推导的数列是否一致。

java 复制代码
class Solution {
    public int fib(int n) {
        if (n == 0) return 0;
        if (n == 1) return 1;             
        int[] dp = new int[n + 1];
        dp[0] = 0;
        dp[1] = 1;
        for (int i = 2; i <= n; i++){
            dp[i] = dp[i - 1] + dp[i - 2];
        }
        return dp[n];
    }
}

可以发现,只需要维护两个数值,不需要记录整个序列。代码如下:

java 复制代码
class Solution {
    public int fib(int n) { // 动态规划
        if (n <= 1) return n;
        int dp0 = 0;
        int dp1 = 1;
        for (int i = 2; i <= n; i++) {
            int sum = dp1 + dp0;
            dp0 = dp1;
            dp1 = sum;
        }
        return dp1;
    }
}

70. 爬楼梯

题目链接:70. 爬楼梯

思路:动态规划五步曲

  1. dpi: 爬到第i层楼梯,有dpi种方法

  2. 递推公式:dpi = dpi - 1 + dpi - 2

    从dpi的定义可以看出,dpi 可以有两个方向推出来。

    首先是dpi - 1,上i-1层楼梯,有dpi - 1种方法,那么再一步跳一个台阶不就是dpi了么。

    还有就是dpi - 2,上i-2层楼梯,有dpi - 2种方法,那么再一步跳两个台阶不就是dpi了么。

    那么dpi就是 dpi - 1与dpi - 2之和!所以dpi = dpi - 1 + dpi - 2

    在推导dpi的时候,一定要时刻想着dpi的定义,否则容易跑偏。

  3. 初始化:dp1 = 1, dp2 = 2

    题目提示:1 <= n <= 45,所以本题不用考虑dp0的初始化!

  4. 从递推公式可以看出是从前向后遍历。

  5. 举例看是否可行

java 复制代码
class Solution {
    public int climbStairs(int n) {
        if (n == 1) return 1;
        // 1、确定dp数组及下标含义
        // dp[i]代表爬到第i层楼梯,有dp[i]种方法
        int[] dp = new int[n + 1];
        // 2、确定递推函数
        // dp[i] = dp[i - 1] + dp[i - 2]
        // 3、确定初始化
        dp[1] = 1;
        dp[2] = 2;
        // 4、确定遍历顺序
        for (int i = 3; i <= n; i++) {
            dp[i] = dp[i - 1] + dp[i - 2];
        }
        return dp[n];
    }
}

本题与斐波那契数相同,可以将空间复杂度从O(n)降为O(1)。


746. 使用最小花费爬楼梯

题目链接:746. 使用最小花费爬楼梯

思路:动态规划五步曲

  1. dpi的定义:到达第i台阶所花费的最少体力为dpi

    题目中说 "你可以选择从下标为 0 或下标为 1 的台阶开始爬楼梯" 也就是相当于 跳到 下标 0 或者 下标 1 是不花费体力的, 从 下标 0 下标1 开始跳就要花费体力了。

  2. 递推公式:dpi = Math.min(dpi - 1 + costi - 1, dpi - 2 + costi - 2)

    可以有两个途径得到dpi,一个是dpi - 1,一个是dpi - 2

    dpi - 1 跳到 dpi 需要花费 dpi - 1 + costi - 1

    dpi - 2 跳到 dpi 需要花费 dpi - 2 + costi - 2

    那么究竟是从dpi - 1跳还是从dpi - 2跳呢?一定是选最小的!

  3. 初始化:dp0 = 0, dp1 = 0。我们认为第一步无需支付费用,所以到第一个台阶和到第二个台阶都是0。

  4. 遍历顺序为从前到后

  5. 举例推导dp数组

    拿cost = 1, 100, 1, 1, 1, 100, 1, 1, 100, 1 ,来模拟一下dp数组的状态变化

    如果代码写出来有问题,就把dp数组打印出来,看看和如上推导的是否一致。

java 复制代码
class Solution {
    public int minCostClimbingStairs(int[] cost) {
        int len = cost.length;
        int[] dp = new int[len + 1];
        // 每次最多走两步,前两个台阶无需支付费用
        dp[0] = 0;
        dp[1] = 0;
        // 计算到达每一层台阶的最小费用
        for (int i = 2; i <= len; i++) {
            dp[i] = Math.min(dp[i - 1] + cost[i - 1], dp[i - 2] + cost[i - 2]);
        }
        return dp[len];
    }
}

还可以优化空间复杂度,因为dpi就是由前两位推出来的,那么也不用dp数组了

java 复制代码
class Solution {
    public int minCostClimbingStairs(int[] cost) {
        int len = cost.length;
        // 每次最多走两步,前两个台阶无需支付费用
        int dp0 = 0;
        int dp1 = 0;
        // 计算到达每一层台阶的最小费用
        for (int i = 2; i <= len; i++) {
            int dp_i = Math.min(dp1 + cost[i - 1], dp0 + cost[i - 2]);
            dp0 = dp1;
            dp1 = dp_i;
        }
        return dp1;
    }
}

相关推荐
benben04410 分钟前
强化学习之DQN算法族(基于gymnasium开发)
算法
小宇宙Zz24 分钟前
Maven依赖冲突
java·服务器·maven
swordbob26 分钟前
NIO的channel中什么是 fd(File Descriptor,文件描述符)
java·开发语言·nio
咖啡八杯1 小时前
GoF设计模式——享元模式
java·spring·设计模式·享元模式
小小工匠1 小时前
Redis - 事务机制:能实现 ACID 属性吗
数据结构·redis·性能优化·并发·持久化
十五喵源码网1 小时前
基于springboot2+vue2的租房管理系统
java·毕业设计·springboot·论文笔记
摇滚侠1 小时前
IDEA 创建 Java 项目 手动整合 SSM 框架
java·ide·intellij-idea
源分享1 小时前
Java线程同步的多种实现方法(非常详细)
java·开发语言·jvm
Flittly1 小时前
【AgentScope Java新手村系列】(10)实战-多Agent天气助手
java·spring boot·spring