码农知识堂 - 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.

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

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

  • 相关阅读:
    Echarts 社区分享
    CMake Tutorial 巡礼(3)_添加库的使用需求
    卖家如何搭上独立站这趟快车
    SpringBoot概述
    Jenkins部署spring boot项目
    Haproxy负载均衡集群
    iOS中的锁
    Mac(M1芯片)安装多个jdk,Mac卸载jdk
    MES管理系统在生产中的应用及智能工厂的构建思路
    PHP毕业设计源代码剧影评|剧评影评系统
  • 原文地址:https://blog.csdn.net/Eibosinu/article/details/133688344
  • 最新文章
  • 攻防演习之三天拿下官网站群
    数据安全治理学习——前期安全规划和安全管理体系建设
    企业安全 | 企业内一次钓鱼演练准备过程
    内网渗透测试 | Kerberos协议及其部分攻击手法
    0day的产生 | 不懂代码的"代码审计"
    安装scrcpy-client模块av模块异常,环境问题解决方案
    leetcode hot100【LeetCode 279. 完全平方数】java实现
    OpenWrt下安装Mosquitto
    AnatoMask论文汇总
    【AI日记】24.11.01 LangChain、openai api和github copilot
  • 热门文章
  • 十款代码表白小特效 一个比一个浪漫 赶紧收藏起来吧!!!
    奉劝各位学弟学妹们,该打造你的技术影响力了!
    五年了,我在 CSDN 的两个一百万。
    Java俄罗斯方块,老程序员花了一个周末,连接中学年代!
    面试官都震惊,你这网络基础可以啊!
    你真的会用百度吗?我不信 — 那些不为人知的搜索引擎语法
    心情不好的时候,用 Python 画棵樱花树送给自己吧
    通宵一晚做出来的一款类似CS的第一人称射击游戏Demo!原来做游戏也不是很难,连憨憨学妹都学会了!
    13 万字 C 语言从入门到精通保姆级教程2021 年版
    10行代码集2000张美女图,Python爬虫120例,再上征途
Copyright © 2022 侵权请联系2656653265@qq.com    京ICP备2022015340号-1
正则表达式工具 cron表达式工具 密码生成工具

京公网安备 11010502049817号