代码随想录刷题|LeetCode 70. 爬楼梯(进阶) 322. 零钱兑换 279.完全平方数 139.单词拆分

news2025/7/19 4:54:22

目录

70. 爬楼梯 (进阶)

思路

爬楼梯

1或2步爬楼梯

多步爬楼梯

322. 零钱兑换

思考

1、确定dp数组及其含义

2、确定递推公式

3、初始化dp数组

4、确定遍历顺序

零钱兑换

先遍历物品,再遍历背包

先遍历背包,再遍历物品

279.完全平方数

思路

完全平方数

创建完全平方和数组,套用公式

将完全平方和融入到公式中

139.单词拆分

思路

1、确定dp数组的含义

2、确定递推公式

3、dp数组如何初始化

4、确定遍历顺序

5、打印dp数组

单词拆分


70. 爬楼梯 (进阶)

题目链接:力扣

思路

        在使用动态规划解决爬楼梯问题的时候,从到达一个台阶有多少种方式入手,使用动态规划是可以很好的解决

        学了完全背包后,可以从另一个角度分析这道题目
        物品:每次可以爬1个台阶、每次可以爬2个台阶
        拿取:可以重复拿取步数
        背包:n阶台阶数
        求值:求的是有多少种方法爬到楼顶(排列数

        这样看起来,这个题目的内容就和求排列数一样了
        和 
377. 组合总和Ⅳ  一样了

        求排列数的注意内容:
        1、初始化dp[0] = 1
        2、遍历时,先遍历背包,再遍历物品
        3、递推公式为 dp[j] = dp[j-weigth[i]]

  • 多步爬楼梯问题:
    • 假设你正在爬楼梯,需要跨过m个台阶才能到达楼顶。每一步你可以跨过1个台阶、2个台阶、3个台阶……m个台阶,直到n层台阶,你有多少种方法可以爬到楼顶呢
  • 多部爬楼梯解决:
    • 按照上面的思路,如果每次爬楼梯的步数是 1 或者 2,那么物品的数组就是【1,2】
    • 如果每次爬楼梯的步数是 1、2、3……m,那么物品的数组就是【1,2,3……m】

爬楼梯

1或2步爬楼梯

// 创建步数数组
class Solution {
    public int climbStairs(int n) {
        // 物品数组
        int[] nums = new int[]{1,2};

        // 创建dp数组
        int[] dp = new int[n+1];

        // 初始化dp数组
        dp[0] = 1;

        // 填充dp数组
        for (int j = 1; j <= n; j++) {
            for (int i = 0; i < nums.length; i++) {
                if (j >= nums[i]) {
                    dp[j] += dp[j-nums[i]];
                }
            }
        }

        return dp[n];
    }
}

// 不创建步数数组
class Solution {
    public int climbStairs(int n) {

        // 创建dp数组
        int[] dp = new int[n+1];

        // 初始化dp数组
        dp[0] = 1;

        // 填充dp数组
        for (int j = 1; j <= n; j++) {
            for (int i = 1; i <= 2; i++) {
                if (j >= i) {
                    dp[j] += dp[j-i];
                }
            }
        }

        return dp[n];
    }
}

多步爬楼梯

class Solution {
    public int climbStairs(int n) {
       

        // 创建dp数组
        int[] dp = new int[n+1];

        // 初始化dp数组
        dp[0] = 1;

        // 填充dp数组
        for (int j = 1; j <= n; j++) { // 遍历背包
            for (int i = 1; i <= m; i++) { // 遍历步数,步数是从1开始的
                if (j >= i) {
                    dp[j] += dp[j-i];
                }
            }
        }

        return dp[n];
    }
}

322. 零钱兑换

题目链接:力扣

思考

        虽然已经做了几天的动态规划,但是每次再拿到动态规划的题目的时候思路还不是很清楚,主要就是如何定义dp[]数组,定义了dp[]数组之后怎么确定递推公式,确定了递推公式之后怎么对数组进行初始化,初始化之后使用那种遍历顺序

        目前来说,定义dp[]数组,基本上都是题目要求什么就定义什么。主要有三类:
        1、纯求背包中物品的价值:
                这种就是一般的背包问题,是比较简单的一种
                递推公式一般是:dp[ j ] = max(dp[ j ],dp[ j - weigth[ i ] ] + value[ i ]);
        2、求物品装满背包的方法数
                这种dp[]数组就是方法数,如果是完全背包,还可能牵扯组合数和排列数
                递归公式一般是:dp[ j ] += dp[ j - weight[ i ] ]
               
由于方法数是逐步积累的,所以初始化应该是dp[ 0 ] = 1
        3、求装满背包时,背包中的物品数
                这种dp[]数组求得是最终背包中的物品个数
                递推公式一般与dp[ j - weight[ i ] ] + 1 ) 有关
                如果是求最大个数:就是dp[ j ] = max(dp[ j ],dp[ j - weight[ i ] ] + 1 );由于是求最大值,初始化时dp[0] = 0,其余的也为0。对应的题目有 474.一和零
                如果是求最小个数:就是dp[ j ] = min(dp[ j ],dp[ j - weight[ i ] ] + 1 );由于是求最小值,初始化时dp[0] = 0,其余的为MAX_VALUE。对应的题目有 322.零钱兑换

下面对本题目进行动态五部曲的分析:

1、确定dp数组及其含义

        题目求什么,就定义什么。题目要求的内容:计算并返回可以凑成总金额所需的 最少的硬币个数 

        dp[ j ]: 凑成总金额为 j 所需要硬币的最小个数

2、确定递推公式

        确定好递推公式后,发现这是上面总结的第三类问题,是求背包中的物品个数,也就是和dp[ j - coins[ i ] ] + 1 ) 有关

        如果说添加了当前物品(coins[i]),那么这个背包中的物品就需要进行添加,背包中的物品个数就是 dp[ j - coins[i] ] + 1
        如果说没有添加当前物品(coins[i]), 那么这个背包中的物品中的个数就不需要进行改变,背包中物品的个数就是 dp[ j ]
        所以说dp[ j ] = min(dp[ j ],dp[ j - coins[ i ] ] + 1 )

3、初始化dp数组

        凑足总金额为0 所需要硬币的个数一定是0,所以dp[0] = 0

        其余的下标的元素必须初始化为一个最大值,否则 min(dp[ j ],dp[ j - coins[ i ] ] + 1 ) 计算出的值就被初始化的值覆盖掉了

4、确定遍历顺序

        不强调组合和排列,先遍历背包或者先遍历物品都是可以的

零钱兑换

先遍历物品,再遍历背包

class Solution {
    public int coinChange(int[] coins, int amount) {

        // 创建dp数组
        int[] dp = new int[amount + 1];

        // 初始化dp数组
        dp[0] = 0;
        for (int i = 1; i <= amount; i++) {
            dp[i] = Integer.MAX_VALUE;
        }

        // 遍历更新dp数组
        for (int i = 0; i < coins.length; i++) { // 先遍历物品
            for (int j = coins[i]; j <= amount; j++) {  // 再遍历背包
                if (dp[j-coins[i]] != Integer.MAX_VALUE) {
                    dp[j] = Math.min(dp[j],dp[j-coins[i]] + 1);
                }             
            }
        }

        return dp[amount] == Integer.MAX_VALUE ? -1 : dp[amount];
    }
}

先遍历背包,再遍历物品

class Solution {
    public int coinChange(int[] coins, int amount) {

        // 创建dp数组
        int[] dp = new int[amount + 1];

        // 初始化dp数组
        dp[0] = 0;
        for (int i = 1; i <= amount; i++) {
            dp[i] = Integer.MAX_VALUE;
        }

        // 遍历更新dp数组
        for (int j = 0; j <= amount; j++) {  // 再遍历背包
            for (int i = 0; i < coins.length; i++) { // 先遍历物品
                if (j - coins[i] >=0 && dp[j-coins[i]] != Integer.MAX_VALUE) {
                    dp[j] = Math.min(dp[j],dp[j-coins[i]] + 1);
                }             
            }
        }

        return dp[amount] == Integer.MAX_VALUE ? -1 : dp[amount];
    }
}

279.完全平方数

题目链接:力扣

思路

        这道题和上面322.零钱兑换是一样的,只不过这道题目的物品数组没有明给,需要自己创建,说着直接套用到公式中

完全平方数

创建完全平方和数组,套用公式

class Solution {
    public int numSquares(int n) {

        // 首先要根据背包创建完全平方和数组
        // 对 n 进行开平方
        double lens = Math.sqrt(n);
        // 对开方出来的数组进行向上取整
        int len = (int)Math.ceil(lens);
        // 创建物品数组
        int[] nums = new int[len + 1]; // 遍历的时候从下标1开始
        for (int i = 0; i <= len; i++) {
            nums[i] = i * i;
        }

        // 创建dp数组
        int[] dp = new int[n + 1];

        // 初始化dp数组
        dp[0] = 0; // 和为0的完全平方数的最少数量是0
        for (int i = 1; i <= n; i++) {
            dp[i] = Integer.MAX_VALUE; //设置最大值,避免被覆盖
        }

        // 遍历更新dp数组
        for (int i = 1; i <= len; i++) {
            for (int j = nums[i]; j <= n; j++) {
                if (dp[j-nums[i]] != Integer.MAX_VALUE) {
                    dp[j] = Math.min(dp[j],dp[j-nums[i]] + 1);
                }
            }
        }

        return dp[n];
    }
}

将完全平方和融入到公式中

class Solution {
    public int numSquares(int n) {

        // 创建dp数组
        int[] dp = new int[n + 1];

        // 初始化dp数组
        dp[0] = 0; // 和为0的完全平方数的最少数量是0
        for (int i = 1; i <= n; i++) {
            dp[i] = Integer.MAX_VALUE; //设置最大值,避免被覆盖
        }

        // 遍历更新dp数组
        for (int i = 1; i <= n; i++) {
            for (int j = i*i; j <= n; j++) {
                if (dp[j-i*i] != Integer.MAX_VALUE) {
                    dp[j] = Math.min(dp[j],dp[j-i*i] + 1);
                }
            }
        }

        return dp[n];
    }
}

139.单词拆分

题目链接:力扣

思路

        比较直接的想法就是使用回溯法不断去组合字符串,比较组合出来的字符串是否与目标字符串相同,显然,回溯这种枚举的方法有很多无效功

        将目标字符串看成背包,将集合中的单词看成物品,集合中的单词能不能组成目标字符串,就是问物品能不能把背包装满。集合中的字符串是可以重复使用的,说明这是一个完全背包问题

1、确定dp数组的含义

dp[i]:字符串长度为i的话,dp[i]为true,表示可以拆分为一个或多个在字典中出现的单词

2、确定递推公式

i 是在遍历目标字符串
j 是每次从0开始遍历目标字符串

对于leetcode, i 遍历到4的时候,j 再对字符串从0遍历,判断s.substring(0,4) -- “leet”在 集合中是存在的,并且dp[0]是为true的,所以此时 下标4 位置上就可以设置为 true,代表leet 是存在在集合中的
下一次,i遍历到8的时候,j 再对字符串从0开始遍历,遍历到 j = 4的时候,判断s.substring(4,8) --"code" 在集合中是存在的,并且dp[4]是为true的,所以此时下标8位置上就可以设置成true,代表 code是存在在集合中的

其实本质上就是使用 true 对字符串进行了分割,如果分割到最后也是true,那就说明这个字符串是可以由集合中的元素组成的

所以判断该元素在集合中存在的条件是 set.contains(s.substring(j,i)) && dp[j]

3、dp数组如何初始化

从递归公式中可以看出,dp[i] 的状态依靠 dp[j]是否为true,那么dp[0]就是递归的根基,dp[0]一定要为true,否则递归下去后面都都是false了

4、确定遍历顺序

本题使用外层for循环遍历物品,内层for遍历背包或者外层for遍历背包,内层for循环遍历物品都是可以的

但本题还有特殊性,因为是要求子串,最好是遍历背包放在外循环,将遍历物品放在内循环

5、打印dp数组

单词拆分

class Solution {
    public boolean wordBreak(String s, List<String> wordDict) {
        HashSet<String> set = new HashSet<>(wordDict);
        
        // 创建dp数组
        boolean[] dp = new boolean[s.length() + 1];

        // 初始化dp数组
        dp[0] = true;

        // 遍历填充dp数组
        for (int i = 1; i <= s.length(); i++) {
            for (int j = 0; j < i && !dp[i]; j++) {
                if (set.contains(s.substring(j,i)) && dp[j]) {
                    dp[i] = true;
                }
            }
        }

        return dp[s.length()];
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/37985.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【操作系统实验】线程的创建+信号量通信

sem_init: 功能&#xff1a;初始化信号量 返回值&#xff1a;创建成功返回0&#xff0c;失败返回-1 参数sem&#xff1a;指向信号量结构的一个指针 参数pshared&#xff1a;不为&#xff10;时此信号量在进程间共享&#xff0c;为0时当前进程的所有线程共享 参数value&#xf…

react context原理

带着问题思考&#xff1a; 1 Provder 如何传递 context&#xff1f;2 三种获取 context 原理 &#xff08; Consumer&#xff0c; useContext&#xff0c;contextType &#xff09;&#xff1f;3 消费 context 的组件&#xff0c;context 改变&#xff0c;为什么会订阅更新 &a…

Vue+iview将表格table以excel文件导出的几种方式

前言 在日常工作中&#xff0c;若是经常跟后台管理系统打交道的朋友想必对导出excel表格这种需求肯定很熟悉吧。不过我也问了身边的一些岗位为后端工程师的朋友&#xff0c;他们说在公司的话一般导出excel表格的工作一般由后端来做&#xff0c;前端只需要调用接口即可。我的话…

AI面试必备-《家居必备的AI精选资源列表》免费分享

本资源介绍 DeepMind科学家和工程师为有兴趣了解更多人工智能、机器学习和其他相关主题的学生整理的教育资源列表。 文末附本书免费获取地址。 内容截图 本资源免费下载地址 链接: https://pan.baidu.com/s/1IkPk0a3q2Z1z4FATG2y7HA?pwdwy3c 提取码: wy3c 往期精品内容推荐 大…

认知电子战 | 无线电中的认知理论

认知的概念 认知(Cognition)是指人认识外界事务的过程 认知本来是用于描述具有生命特征的物种的,借用于机器或系统上,就是指将认知的思想应用于机器上。 生物的认知特点: 感觉思考、推理、问题解答判断记忆分别对应于系统认知特点: 感知(各种传感器)机器学习算法、基…

算法提升:图的启发式搜索算法(A算法、A*算法)

启发式搜索算法 目录 概念 A算法 A*算法 概念 启发式搜索(Heuristically Search)又称为有信息搜索(Informed Search)&#xff0c;它是利用问题拥有的启发信息来引导搜索&#xff0c;达到减少搜索范围、降低问题复杂度的目的&#xff0c;这种利用启发信息的搜索过程称为启发…

修改 Git 已经提交记录的 用户名 和邮箱

修改 Git 已经提交记录的 用户名 和邮箱 有关 Git 和版本控制的常见问题。 如何更改提交的作者姓名/电子邮件&#xff1f; 在我们进入解决方案之前&#xff0c;让我们找出您到底想要完成什么&#xff1a; 在提交之前更改作者信息在提交后更改作者信息&#xff08;即历史提交…

常见的前端安全问题(xss / csrf / sql / ddos / cdn...)

目录 1. xss&#xff08;Cross Site Scripting&#xff09;跨站脚本攻击 1.1 持久型&#xff08;存储型&#xff09;攻击 / 非持久型&#xff08;反射型&#xff09;攻击 是什么&#xff1f; 1.2 xss 出现的场景&#xff1f;造成的危害&#xff1f; 1.3 防御 xss&#xff0…

three.js之Geometry顶点、颜色数据与三角面

文章目录简介顶点对于代码的解释颜色对于代码的解释三角面专栏目录请点击 简介 Geometry与BufferGeometry表达的含义相同&#xff0c;只是对象的结构不同three.js渲染的时候会先把Geometry转化为BufferGeometry在解析几何体顶点数据进行渲染 顶点 <!DOCTYPE html> <…

基于JavaWeb的药品进销存管理系统(JSP)

目 录 绪论 1 1.1 本课题的研究背景 1 1.2 国内外研究现状 1 1.3 本课题的主要工作 2 1.4 目的和意义 2 开发工具及技术 3 2.1 开发工具 3 2.1.1 MyEclipse 3 2.1.2 Tomcat 3 2.1.3 Mysql 3 2.2 开发技术 4 2.2.1 JSP 4 2.2.2 MyBatis 4 2.2.3 JavaScript 4 2.2.4 jQuery以及j…

机械原理-试题及答案

模拟试题八&#xff08;机械原理A&#xff09; 一、判断题&#xff08;10分&#xff09;[对者画√&#xff0c;错者画 ] 1、对心曲柄滑块机构都具有急回特性。&#xff08; &#xff09; 2、渐开线直齿圆柱齿轮的分度圆与节圆相等。&#xff08; &#xff09; 3、当两直齿圆柱齿…

Spring Cloud OpenFeign - 日志配置

项目源码地址&#xff1a;https://download.csdn.net/download/weixin_42950079/87168704 OpenFeign 有 4 种日志级别&#xff1a; NONE: 不记录任何日志&#xff0c;是OpenFeign默认日志级别&#xff08;性能最佳&#xff0c;适用于生产环境&#xff09;。BASIC: 仅记录请求方…

[附源码]计算机毕业设计JAVA人力资源管理系统论文2022

[附源码]计算机毕业设计JAVA人力资源管理系统论文2022 项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM…

QT对象树和菜单操作

前言 可以与MFC框架进行比较&#xff0c;总结彼此的相同点和不同点&#xff0c;在此基础上再去学习其他的界面框架&#xff0c;达到增量学习的境界。 一.对象树 优点&#xff1a;当父对象被析构时&#xff0c;如果子对象没有被析构&#xff0c;QT的对象树机制会去析构它&…

pandas索引操作、赋值操作、排序以及Series排序和DataFrame排序

一、pandas索引操作 索引操作&#xff0c;使用索引选取序列和切片选择数据&#xff0c;也可以直接使用列名、行名称&#xff0c;或组合使用 直接使用行列索引&#xff1a;行列索引名顺序为先列再行&#xff0c;使用指定行列索引名&#xff0c;不能使用下标loc[行索引名&#…

MySQL数据库表空间回收问题

MySQL数据库表空间回收问题1. MySQL表空间回收2. MySQL表空间设置3. MySQL删除数据流程4. MySQL数据页空洞问题1. MySQL表空间回收 我们经常会发现一个问题&#xff0c;就是把表数据删除以后发现&#xff0c;数据文件大小并没有变化&#xff0c;这就是标题中所说的MySQL数据库…

Numpy数组中的维度和轴

维度究竟是行数还是列数&#xff1f; m维行向量&#xff1a;m维表示一行中有m列&#xff0c;由于是行向量&#xff0c;所以是1行m列n维列向量&#xff1a;n维表示一行中有n行&#xff0c;由于是列向量&#xff0c;所以是n行1列m维向量&#xff1a;看书的习惯了&#xff0c;一般…

APS生产计划排产 — 排产结果拉动模具工装需求计划

APS生产计划排产系统&#xff0c;对所有资源具有同步的&#xff0c;实时的&#xff0c;具有约束能力的&#xff0c;模拟能力&#xff0c;不论是物料&#xff0c;机器设备&#xff0c;人员&#xff0c;供应&#xff0c;客户需求&#xff0c;运输等影响计划因素。不论是长期的或短…

【CNN-SVM回归预测】基于CNN-SVM实现数据回归预测附matlab代码

✅作者简介&#xff1a;热爱科研的Matlab仿真开发者&#xff0c;修心和技术同步精进&#xff0c;matlab项目合作可私信。 &#x1f34e;个人主页&#xff1a;Matlab科研工作室 &#x1f34a;个人信条&#xff1a;格物致知。 更多Matlab仿真内容点击&#x1f447; 智能优化算法 …

JavaWeb开发之——多表查询(21)

一 概述 多表查询—简介多表查询—内连接&外连接多表查询—子查询 二 多表查询—简介 2.1 概念 多表查询顾名思义就是从多张表中一次性的查询出我们想要的数据。 2.2 SQL数据准备 DROP TABLE IF EXISTS emp; DROP TABLE IF EXISTS dept; # 创建部门表 CREATE TABLE de…