代码随想录day32,动态规划part1

理论基础

动态规划问题的一般形式就是求最值 。比如说让你求最长递增子序列呀,最小编辑距离。求解动态规划的核心问题是穷举。因为要求最值,肯定要把所有可行的答案穷举出来,然后在其中找最值。

穷举所有可行解需要你熟练掌握递归思维,只有列出正确的「状态转移方程」 ,才能正确地穷举。判断算法问题是否具备「最优子结构」 ,是否能够通过子问题的最值得到原问题的最值 。动态规划问题存在「重叠子问题」,如果暴力穷举的话效率会很低,所以需要你使用「备忘录」或者「DP table」来优化穷举过程,避免不必要的计算。

以上提到的重叠子问题、最优子结构、状态转移方程就是动态规划三要素。

明确「状态」-> 明确「选择」 -> 定义 dp 数组/函数的含义

我认为动态规划就是你能不能根据这道题去抽象出函数方程,这个方程是每一个状态一定是由上一个状态推导出来的,根据这个公式去写代码。

** 509. 斐波那契数 **

题目

力扣题目链接

斐波那契数 (通常用 F(n) 表示)形成的序列称为 斐波那契数列 。该数列由 01 开始,后面的每一项数字都是前面两项数字的和。也就是:

复制代码
F(0) = 0,F(1) = 1
F(n) = F(n - 1) + F(n - 2),其中 n > 1

给定 n ,请计算 F(n)

示例1:

复制代码
输入:n = 2
输出:1
解释:F(2) = F(1) + F(0) = 1 + 0 = 1

思路

如果是直接递归呢,那么就是照着函数方程去写代码:

java 复制代码
int fib(int n){
    if(n==0||n=1){
        return n;
    }
    return fib(n-1)+fib(n-2);
}

本质就是递归树,就是说想要计算原问题 f(20),我就得先计算出子问题 f(19)f(18),然后要计算 f(19),我就要先算出子问题 f(18)f(17),以此类推。最后遇到 f(1) 或者 f(2) 的时候,结果已知,就能直接返回结果,递归树不再向下生长了。

递归算法的时间复杂度是用子问题个数 * 解决一个问题需要的时间。即为递归树的总结点数2n * 加法的复杂度O(1)=O(2n);

但是存在大量重复计算:比如 f(18) 被计算了两次,而且以 f(18) 为根的这个递归树体量巨大,多算一遍,会耗费大量的时间。更何况还不止 f(18) 这一个节点被重复计算,所以这个算法效率很差。这就是重叠子问题

为了解决这个问题,加入了备忘录,就是每次算出某个子问题的答案后顺便记到「备忘录」里;每次遇到一个子问题别急着计算,先去「备忘录」里查一查,如果发现之前已经解决过这个问题了,直接把答案拿出来用,不要再耗时去计算了。可以看看代码的解释

代码

java 复制代码
int fib(int n){
    //创建一维数组,因为数组的索引从0开始,所以需要n+1个空间
    int[] memo = new int [n+1];
    //备忘录初始化为-1,是一个未计算标记,子问题的有效结果不会是-1。存储已解决的子问题结果,避免重复计算
    Arrays.fill(memo,-1);
    //memo是备忘录,n是子问题规模,比如斐波那契中的目标项数、凑零钱中的目标金额)。
    return dp(memo,n);
}

//带备忘录进行递归
int dp(int[]memo,int n){
    if(n==0||n==1){
        return n;
    }
    //备忘录的作用
     if(memo[n] !=-1){
         return memo[n];// 已经计算过,不用再计算了
     }
    //返回结果前,存入备忘录
    memo[n] = dp(memo,n-1)+dp(memo,n-2);
    return memo[n];
}

70. 爬楼梯

题目

力扣题目链接

假设你正在爬楼梯。需要 n 阶你才能到达楼顶。每次你可以爬 12 个台阶。你有多少种不同的方法可以爬到楼顶呢?

示例 1:

复制代码
输入:n = 2
输出:2
解释:有两种方法可以爬到楼顶。
1. 1 阶 + 1 阶
2. 2 阶

思路

  • 爬到第一层楼梯有一种方法,爬到二层楼梯有两种方法。

  • 那么第一层楼梯再跨两步就到第三层 ,第二层楼梯再跨一步就到第三层。

  • 所以到第三层楼梯的状态可以由第二层楼梯 和 到第一层楼梯状态推导出来

这样能看出是重叠子问题

  • 首先确定递归的数组dp[i]: 爬到第i层楼梯,有dp[i]种方法

  • 然后推测上i-1层楼梯,有dp[i - 1]种方法,再跳一个台阶就是dp[i],比如到2层有2种方法,每种再走1步都能到3层 → 贡献 2

  • 上i-2层楼梯,有dp[i -2]种方法,因为到 i-2 层有 dp[i-2] 种方式,再走2步到i层。比如到1层有 1种 方法,再走2步到3层 → 贡献 1

  • 所以:dp[i] = dp[i-1] + dp[i-2]

dp[1] = 1,dp[2] = 2,然后从i = 3开始递推,那就会发现他的公式和斐波那契数列公式一样。

代码

java 复制代码
class Solution{
    int climbStairs(int n) {
    int[] memo = new int[n+1];
    Arrays.fill(memo, -1);
    return dp(memo, n);
}
    int dp(int[] memo, int n) {
    if (n == 1) return 1;  // 到1层:1种方法
    if (n == 2) return 2;  // 到2层:2种方法
    
    if (memo[n] != -1) return memo[n];
    
    memo[n] = dp(memo, n-1) + dp(memo, n-2);  // 到n层 = 到n-1层 + 到n-2层
    return memo[n];
    }
}

746. 使用最小花费爬楼梯

题目

力扣题目链接

给你一个整数数组 cost ,其中 cost[i] 是从楼梯第 i 个台阶向上爬需要支付的费用。一旦你支付此费用,即可选择向上爬一个或者两个台阶。

你可以选择从下标为 0 或下标为 1 的台阶开始爬楼梯。

请你计算并返回达到楼梯顶部的最低花费。

示例 1:

复制代码
输入:cost = [10,15,20]
输出:15
解释:你将从下标为 1 的台阶开始。
- 支付 15 ,向上爬两个台阶,到达楼梯顶部。总花费为 15 。

思路

  • dp[i]的定义:到达第i台阶所花费的最少体力为dp[i];

  • dp[i - 1] 跳到 dp[i] 需要花费 dp[i - 1] + cost[i - 1]

  • dp[i - 2] 跳到 dp[i] 需要花费 dp[i - 2] + cost[i - 2]

  • 从dp[i - 1]和dp[i - 2]中选一个最小的跳

  • 所以dp[i] = min(dp[i - 1] + cost[i - 1], dp[i - 2] + cost[i - 2]);

题目提到:"你可以选择从下标为 0 或下标为 1 的台阶开始爬楼梯" 也就是说 到达 第 0 个台阶是不花费的,但从 第0 个台阶 往上跳的话,需要花费 cost[0]。

所以初始化 dp[0] = 0,dp[1] = 0;

代码

java 复制代码
class Solution {
    public int minCostClimbingStairs(int[] cost) {
        int len = cost.length;
        int[] dp = new int[len + 1];

      //从下标为0或下标为1的台阶开始,因此支付费用为0
        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];
    }
}
相关推荐
楠秋9201 小时前
代码随想录算法训练营第三十二天| 509. 斐波那契数 、 70. 爬楼梯 、746. 使用最小花费爬楼梯
数据结构·算法·leetcode·动态规划
㓗冽1 小时前
最大效益(二维数组)-基础题76th + 螺旋方阵(二维数组)-基础题77th + 方块转换(二维数组)-基础题78th
数据结构·算法
Ivanqhz1 小时前
数据流分析的核心格(Lattice)系统
开发语言·javascript·后端·python·算法·蓝桥杯·rust
麻辣香蝈蝈2 小时前
【入门】职场如何沟通以及开发一个功能
java
he___H2 小时前
jvm41-47回
java·开发语言·jvm
琛説2 小时前
⚡PitchPPT:将PPT导出为高清全图PPT,并控制PPT文件大小在固定MB/GB以内【解析算法原理 · 作者谈】
windows·python·算法·github·powerpoint
骑猪撞地球QAQ2 小时前
Java在导出excel时中添加图片导出
java·开发语言·excel
We་ct2 小时前
LeetCode 25. K个一组翻转链表:两种解法详解+避坑指南
前端·算法·leetcode·链表·typescript
好家伙VCC2 小时前
# 发散创新:基于 Go 语言打造高性能服务网格的实践与突破在微服务架构
java·python·微服务·架构·golang