码农知识堂 - 1000bd
  •   Python
  •   PHP
  •   JS/TS
  •   JAVA
  •   C/C++
  •   C#
  •   GO
  •   Kotlin
  •   Swift
  • LeetCode刷题笔记【35】:动态规划专题-7(爬楼梯、零钱兑换、完全平方数)


    文章目录

    • 前置知识
    • 70. 爬楼梯 (进阶)
      • 题目描述
      • 解题思路
      • 代码
    • 322. 零钱兑换
      • 题目描述
      • 解题思路
      • 代码
    • 279.完全平方数
      • 题目描述
      • 解题思路
      • 代码
    • 总结

    前置知识

    今天的三道题都聚焦完全背包问题, 关于完全背包, 基础性的思路可以参考上一篇文章
    本文的很多操作就是在完全背包的基础上进行修改.

    参考文章:
    LeetCode刷题笔记【29】:动态规划专题-1(斐波那契数、爬楼梯、使用最小花费爬楼梯)
    LeetCode刷题笔记【30】:动态规划专题-2(不同路径、不同路径 II)
    LeetCode刷题笔记【31】:动态规划专题-3(整数拆分、不同的二叉搜索树)
    LeetCode刷题笔记【32】:动态规划专题-4(二维背包问题、一维背包问题、分割等和子集)
    LeetCode刷题笔记【33】:动态规划专题-5(最后一块石头的重量 II、目标和、一和零)
    LeetCode刷题笔记【34】:动态规划专题-6(完全背包、零钱兑换II、组合总合IV)

    70. 爬楼梯 (进阶)

    题目描述

    在这里插入图片描述

    LeetCode链接:https://leetcode.cn/problems/climbing-stairs/description/

    解题思路

    这道题我们之前有做过, 用的是简单动态规划的思想, 可以参考前文(点击文字跳转).

    但是现在用完全背包的视角来看, 可以视为背包大小为n, 有大小为1和2的两种数量不限的物品, 要让我们求有多少种方法可以装满背包.

    需要注意的点为:

    1. 递推公式为dp[j] = dp[j] + dp[j-i];
    2. dp[0] = 1;
    3. 因为要求的是排列, 所以外层遍历背包容量j, 内层遍历物品i

    代码

    动态规划代码:

    class Solution {
    public:
        int climbStairs(int n) {
            if(n==0 || n==1)
                return 1;
            int first=1, second=1;
            for(int i=2; i<=n; ++i){
                int tmp = first + second;
                first = second;
                second = tmp;
            }
            return second;
        }
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    完全背包代码:

    class Solution {
    public:
        int climbStairs(int n) {
            vector<int> dp(n+1, 0);
            dp[0] = 1;
            for(int j=1; j<=n; ++j){
                for(int i=1; i<=2; ++i){
                    if(j-i>=0)
                        dp[j] = dp[j] + dp[j-i];
                }
            }
            return dp.back();
        }
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    可以看出: 完全背包解法的代码和注意事项都和<377. 组合总和 Ⅳ>很像.

    322. 零钱兑换

    题目描述

    在这里插入图片描述

    LeetCode链接:https://leetcode.cn/problems/coin-change/description/

    解题思路

    如果之前不了解动态规划和完全背包的话, 这道题难度是比较高的.
    但是经历过之前这一波的毒打, 我们可以一眼看出这一定要用完全背包做(给无限量的xx, 需要凑齐xx量).

    前面遇到过<518. 零钱兑换II>, 我们要注意对比这两道题, 会发现无论是初始化方式, 递推公式, 判断条件, 还有是否需要注意内外层顺序, 二者都有差别.

    关键就是, <518. 零钱兑换II>要求的是"有多少种方法凑够", 而本题要求的是"凑够给定的数额最少需要多少"

    需要注意的有这几点:

    1. 初始化时dp数组其他元素为INT_MAX, dp[0]=0;
    2. 递推公式为: dp[j] = min(dp[j], 1+dp[j-coins[i]]);
    3. 因为有无法填满背包的情况, 所以在过程中要加判断if(dp[j-coins[i]]!=INT_MAX)
    4. 要求的是"最少需要的硬币个数", 而不是"需要最少的硬币个数的组合/排列", 所以不用考虑内外层先遍历物品i还是背包容量j的问题, 怎么遍历结果都一样.

    以题目中示例1为例, dp数组更新过程如下:
    以题目中示例1为例
    可以看到, 当时推导的时候还在右上角纠结了一下内外层, 其实没有必要, 都一样.

    PS: 尤其需要注意的是第3点, 因为可能会有凑不齐的情况, 比如题目给的示例2和示例3, 以实例2为例:
    在这里插入图片描述
    只有加了if(dp[j-coins[i]]!=INT_MAX)的判断, 才可以将这种凑不齐的情况找出来, 并return -1

    代码

    class Solution {
    public:
        int coinChange(vector<int>& coins, int amount) {
            vector<int> dp(amount+1, INT_MAX);
            dp[0] = 0;
            for(int i=0; i<coins.size(); ++i){
                for(int j=1; j<=amount; ++j){
                    if(j-coins[i]>=0 && dp[j-coins[i]]!=INT_MAX){
                        dp[j] = min(dp[j], 1+dp[j-coins[i]]);
                    }
                }
            }
            if(dp[amount]==INT_MAX)
                return -1;
            return dp[amount];
        }
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    // 优化了一下
    class Solution {
    public:
        int coinChange(vector<int>& coins, int amount) {
            vector<int> dp(amount+1, INT_MAX);
            dp[0] = 0;
            for(int i=0; i<coins.size(); ++i){
                for(int j=coins[i]; j<=amount; ++j){
                    if(dp[j-coins[i]]!=INT_MAX){
                        dp[j] = min(dp[j], 1+dp[j-coins[i]]);
                    }
                }
            }
            if(dp[amount]==INT_MAX)
                return -1;
            return dp[amount];
        }
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    279.完全平方数

    题目描述

    在这里插入图片描述

    LeetCode链接:https://leetcode.cn/problems/perfect-squares/description/

    解题思路

    思路和上一题基本一样, 只不过是刚才的物品是coins, 现在物品是i*i, 并且因为有1*1=1, 所以不用考虑凑不齐的问题.

    代码

    class Solution {
    public:
        int numSquares(int n) {
            vector<int> dp(n+1, INT_MAX);
            dp[0] = 0;
            vector<int> nums;
            for(int i=1; ; ++i){
                if(i*i <= n)
                    nums.push_back(i*i);
                else
                    break;
            }
            for(int i=0; i<nums.size(); ++i){
                for(int j=nums[i]; j<=n; ++j){
                    dp[j] = min(dp[j], 1+dp[j-nums[i]]);
                }
            }
            return dp.back();
        }
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    // 让写法简洁一些
    class Solution {
    public:
        int numSquares(int n) {
            vector<int> dp(n+1, INT_MAX);
            dp[0] = 0;
            for(int i=1; i*i<=n; ++i){
                for(int j=i*i; j<=n; ++j){
                    dp[j] = min(dp[j], 1+dp[j-i*i]);
                }
            }
            return dp.back();
        }
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    总结

    今天是昨天的完全背包问题的进一步深化和拓展, 需要着重进行比较和辨析.
    尤其是今天的零钱兑换, 和昨天的零钱兑换IV.

    经过这段时间的背包问题折磨, 基本可以比较顺畅的想到背包问题了, 但是在实践过程中, 如何快速抽象出物品, 背包容量等要素, 同时避免"手里是锤子, 看啥都是钉子"的思维定式, 还是要在不断地训练中提升.

    本文参考:
    爬楼梯
    零钱兑换
    完全平方数

  • 相关阅读:
    互斥锁,自旋锁,读写锁
    计算机网络_2.1 物理层概述
    华为机试 - 连接器问题
    SpringBoot+Vue实现前后端分离灾情救援系统
    看一眼就会马上收藏的宝藏设计网站
    Cpolar+Tipas:在Ubuntu上搭建私人问答网站,为您提供专业的问题解答
    9月23日前,洪山区2022年智能制造与两化融合发展专项资金项目申报类型、条件
    想把iPad作为扩展屏,确发现macOS monterey随航功能不见了
    【2022年11月22日提高A组】数论计算【SPFA】【数学】
    华为云云耀云服务器L实例评测|了解配置和管理L型云服务器
  • 原文地址:https://blog.csdn.net/Eibosinu/article/details/133688344
  • 最新文章
  • 【JVM】编译执行与解释执行的区别是什么?JVM 使用哪种方式?
    用 Hashids 优雅解决 C 端自增 ID 暴露问题
    V8引擎 精品漫游指南--Ignition篇(上) 指令 栈帧 槽位 调用约定 内存布局 基础内容
    LLVM Pass快速入门(四):代码插桩
    milkup:桌面端 markdown AI续写和即时渲染
    基于项目工程构建SBOM(软件物料清单)的研究
    鸿蒙应用开发UI基础第二节:鸿蒙应用程序框架核心解析与实操
    .NET 中如何快速实现 List 集合去重?
    扣子Coze实战:从0到1打造抖音+小红书热点监控智能体
    浅谈数据访问层
  • 热门文章
  • 十款代码表白小特效 一个比一个浪漫 赶紧收藏起来吧!!!
    奉劝各位学弟学妹们,该打造你的技术影响力了!
    五年了,我在 CSDN 的两个一百万。
    Java俄罗斯方块,老程序员花了一个周末,连接中学年代!
    面试官都震惊,你这网络基础可以啊!
    你真的会用百度吗?我不信 — 那些不为人知的搜索引擎语法
    心情不好的时候,用 Python 画棵樱花树送给自己吧
    通宵一晚做出来的一款类似CS的第一人称射击游戏Demo!原来做游戏也不是很难,连憨憨学妹都学会了!
    13 万字 C 语言从入门到精通保姆级教程2021 年版
    10行代码集2000张美女图,Python爬虫120例,再上征途
小工具 小游戏
Copyright © 2022 侵权请联系2656653265@qq.com    京ICP备2022015340号-1

京公网安备 11010502049817号