动态规划篇-01:爬楼梯

news/2024/7/24 9:29:17 标签: 动态规划, 算法

本文为力扣70:爬楼梯的详细解析。

虽然这道题的标签是“简单”,但是只有简单的题才能让我们专注于这类题的解题框架上。

一般来说动态规划会有三种解法:暴力解法、使用了备忘录自上而下的递归解法、使用了数组的自下而上的迭代解法。接下来我会对这三种解法逐一演示

70:爬楼梯

根据上文 动态规划篇-00:解题思想与框架 首先我们要明确 [状态转移方程],这样我们就能写出最基础的暴力解法了。

状态转移方程

思考状态转移方程的思路:base case → 明确状态 → 明确路径 → 定义dp函数

base case

在此题中,最小子问题或者说是边界条件就是 “楼梯阶梯数为1或者2的时候”。这个边界问题是根据题意 “你每次可以爬1或2个台阶” 得来的

明确状态

“原问题和子问题中会变化的变量”

此处的 “选择” 为 爬到楼顶的方法

确定选择

“导致状态变化的行为”

此处 “状态” 为楼梯的阶数。正是因为楼梯阶数的变化,导致“爬到楼顶的方法”产生了变化。

定义dp函数

根据题意,自然而然定义 dp(n) 为 “爬上n阶台阶的方法”

我们回想上一篇文章,“[分解问题]思路扩展一下就是动态规划算法”。那么dp(n)的子问题是什么?

我们最后一步是爬上第n阶阶梯,这一步之前我们需要决定是爬1步到n层(dp(n-1))还是爬两步到n层(dp(n-2))。

于是我们根据子问题和上述分析得出了状态转移方程:

状态转移方程中的 “状态转移” 在此处的表现就是:f(n)这个状态可以由 f(n-1) 和 f(n-2) 转移而来

有了状态转移方程就能写出暴力解法了

暴力递归

class Solution {
    public int climbStairs(int n) {   
        return dp(n);
    }
    //定义一个方法dp,表示:有多少种方法爬上n阶阶梯
    public int dp(int n){
        //明确base case
        if(n == 1 || n == 0){
            return 1;
        }
        //写出状态转移方程
        return dp(n-1) + dp(n-2);
    }
}

但是这种解法是通过递归所有可能的情况来得出最终解,时间复杂度较高。这是因为存在大量“重叠子问题”。

比如想要计算 dp(6) ,就要计算dp(5) 和 dp(4)。计算dp(5) 又要计算dp(4) 和 dp(3)。但是dp(4) 已经在计算dp(6) 中计算过了。如果我们用一个备忘录把计算过的结果记录下来,需要的时候直接提取而不是重新计算,那么时间复杂度就会降下来。

使用了备忘录自上而下的递归解法

class Solution {
    public int climbStairs(int n) {
        //定义一个备忘录数组用于记录数据
        int[] memo = new int[n+1];
        return dp(n,memo);
    }
    
    //定义一个方法dp,表示:有多少种方法爬上n阶阶梯
    public int dp(int n,int[] memo){
        //先将备忘录初始化为 -1
        Arrays.fill(memo,-1);
        //base case
        if(n == 1 || n == 0){
            return 1;
        }
        if(memo[n] != -1){
            return memo[n];
        }
        //写出状态转移方程,并更新备忘录
        memo[n] = dp(n-1,memo) + dp(n-2,memo);
        return memo[n];
    }
}

在这种方法中我们使用了一个数组 memo 来记录结果。例如 memo[5] 表示爬上5阶台阶的爬法,对应 dp(5) 的结果。

但是这里有两个问题需要注意:memo数组的长度应该是多少?memo 数组的初始化应该是多少?

问题1: memo数组的长度应该是多少?

我们设定memo数组的长度为 n+1,是为了让memo[n] 对应 dp[n] 的结果,如果memo数组的长度为n的话,memo[n-1] 对应dp(n),dp(0)就只能对应 memo[-1]了。

数组的索引怎么能是 -1 呢?

问题2: memo 数组的初始化应该是多少?

初始化值应该设定为一个 dp 函数无法取到的值。

如果把memo数组中的元素初始化为1。dp(1) = 1,memo[1] = 1。那么我怎么直到这个返回值是dp数组的返回值,还是memo数组初始化的返回值呢?

所以说,初始化值应该设定为一个 dp 函数无法取到的值。

使用了dp数组的自下而上的迭代解法

我们定义一个dp数组,数组元素即为结果。比如dp[n] 定义为 : 爬上n阶阶梯的方法

class Solution {
    public int climbStairs(int n) {
        /*dp[i] 表示的是登录 i层阶梯的方法数量*/
        int[] dp = new int[n+1];
        if(n <= 2){
            return n;
        }
        //base case
        dp[0] = 0;dp[1] = 1;dp[2] = 2;
        for(int i = 3;i <= n;i++){
            dp[i] = dp[i-1] + dp[i-2];
        }
        return dp[n];
    }
}

问题1:迭代解法和递归解法的区别

两者的定义起始并无区别。

dp(n) 的定义为 “爬上n阶阶梯的爬法”,dp[n]也是一样的。

dp(n) 是递归解法。比如想要算出dp(4),要算出dp(3) 和dp(2),依次往下,层层递进。直到递进到base case后,拿到数据,在层层回归,得到dp(4)。

而dp[n] 的迭代解法是自下而上的。逐步算出dp[1]、dp[2]、dp[3].....直到算出dp[n]。


那么对于其他题也是如此吗?我们来试试用这个方法算下一道题。


http://www.niftyadmin.cn/n/5323277.html

相关文章

学习Java API(一):基础知识点一文通✅

推荐阅读 智能化校园&#xff1a;深入探讨云端管理系统设计与实现&#xff08;一&#xff09; 智能化校园&#xff1a;深入探讨云端管理系统设计与实现&#xff08;二&#xff09; 文章目录 推荐阅读API文档注释String类创建字符串拼接字符串格式化字符串String方法substring(…

exec函数簇和守护进程

目录 一、exec函数族 二、守护进程 三、GDB调试多进程程序 一、exec函数族 exec函数使得进程当前内容被指定的程序替换。 示例&#xff1a; 运行结果&#xff1a; 代码就相当于执行命令&#xff1a;ls -a -l ./ 二、守护进程 举例&#xff1a; 运行结果&#xff1a; 举例…

宠物服务新篇章:预约小程序带来的变革

随着科技的进步和互联网的普及&#xff0c;小程序已经成为了一种非常受欢迎的应用形式。对于宠物门店来说&#xff0c;开发一个预约小程序可以大大提高客户体验和管理效率。下面是一份宠物门店预约小程序的开发指南。 浏览器搜索乔拓云&#xff0c;登录乔拓云网后台&#xff0c…

springboot 整合 actuator监控详情

SpringBoot自带监控功能Actuator&#xff0c;可以帮助实现对程序内部运行情况监控&#xff0c;比如监控状况、Bean加载情况、环境变量、日志信息、线程信息等 pom文件中添加 <!-- actuator start--> <dependency><groupId>org.springframework.boot</gr…

【记录】求职经历

目标岗位&#xff1a;嵌入式开发 1. 线上笔试 常用算法&#xff0c;比如动态规划、递归等标准模板库&#xff08;STL&#xff09;C 11新特性LeetCode刷题牛客刷题 2. 技术一面 3. 技术二面 4. 主管面 5. HR面

Grind75第9天 | 733.图像渲染、542.01矩阵、1235.规划兼职工作

733.图像渲染 题目链接&#xff1a;https://leetcode.com/problems/flood-fill 解法&#xff1a; 可以用深度优先搜索和广度优先搜索。 深度优先搜索。每次搜索到一个方格时&#xff0c;如果其与初始位置的方格颜色相同&#xff0c;就将该方格的染色&#xff0c;然后继续对…

[paddle]paddlehub部署paddleocr的hubserving服务

步骤如下&#xff1a; 第一步&#xff1a;首先需要安装好paddleocr环境已经paddlehub环境 第二步&#xff1a;下载paddleocr源码&#xff1a; git clone https://github.com/PaddlePaddle/PaddleOCR.git 然后切换到paddocr目录执行 新建个文件夹叫Inference把paddleocr模型…

行为型设计模式——迭代器模式

迭代器模式 迭代器模式也是非常的简单&#xff0c;定义如下&#xff1a; 提供一个对象来顺序访问聚合对象中的一系列数据&#xff0c;而不暴露聚合对象的内部表示。 相信大家都使用过类似下面的迭代器&#xff1a; List<String> list new ArrayList<>(); Iterat…