今日主要总结一下动态规划完全背包的一道题目,322. 零钱兑换

题目:322. 零钱兑换

Leetcode题目地址
题目描述:
给你一个整数数组 coins ,表示不同面额的硬币;以及一个整数 amount ,表示总金额。

计算并返回可以凑成总金额所需的 最少的硬币个数 。如果没有任何一种硬币组合能组成总金额,返回 -1 。

你可以认为每种硬币的数量是无限的。

示例 1:

输入:coins = [1, 2, 5], amount = 11
输出:3
解释:11 = 5 + 5 + 1

示例 2:

输入:coins = [2], amount = 3
输出:-1

示例 3:

输入:coins = [1], amount = 0
输出:0

提示:

1 <= coins.length <= 12
1 <= coins[i] <= 231 - 1
0 <= amount <= 104

本题重难点

这是一道典型的背包问题,本题给定的数组里面的元素可以重复取,所以这是一个完全背包。

在动态规划:518.零钱兑换II (opens new window)中我们已经兑换一次零钱了,这次又要兑换,套路不一样!

题目中说每种硬币的数量是无限的,可以看出是典型的完全背包问题。

动规五部曲分析如下:

  1. 确定dp数组以及下标的含义
    dp[j]:凑足总额为j所需钱币的最少个数为dp[j]

  2. 确定递推公式
    凑足总额为j - coins[i]的最少个数为dp[j - coins[i]],那么只需要加上一个钱币coins[i]即dp[j - coins[i]] + 1就是dp[j](考虑coins[i])
    所以dp[j] 要取所有 dp[j - coins[i]] + 1 中最小的。
    递推公式:dp[j] = min(dp[j - coins[i]] + 1, dp[j]);

  3. dp数组如何初始化
    首先凑足总金额为0所需钱币的个数一定是0,那么dp[0] = 0;
    其他下标对应的数值呢?
    考虑到递推公式的特性,dp[j]必须初始化为一个最大的数,否则就会在min(dp[j - coins[i]] + 1, dp[j])比较的过程中被初始值覆盖。
    所以下标非0的元素都是应该是最大值。
    代码如下:


vector<int> dp(amount + 1, INT_MAX);
dp[0] = 0;

其实判断如何初始化重要一点就是看dp是取之前状态最小值还是最大值,如果最小值就大概率INT_MAX,最大值就大概率0。

  1. 确定遍历顺序
    本题求钱币最小个数,那么钱币有顺序和没有顺序都可以,都不影响钱币的最小个数。
    所以本题并不强调集合是组合还是排列。
    如果求组合数就是外层for循环遍历物品,内层for遍历背包。
    如果求排列数就是外层for遍历背包,内层for循环遍历物品。

    在动态规划之前文章我们讲过了求组合数是一文搞懂完全背包之518. 零钱兑换 II问题,求排列数是一文搞懂完全背包之377. 组合总和 Ⅳ问题
    所以本题的两个for循环的关系是:外层for循环遍历物品,内层for遍历背包或者外层for遍历背包,内层for循环遍历物品都是可以的!
    那么我采用coins放在外循环,target在内循环的方式。
    本题钱币数量可以无限使用,那么是完全背包。所以遍历的内循环是正序
    综上所述,遍历顺序为:coins(物品)放在外循环,target(背包)在内循环。且内循环正序。

  2. 举例推导dp数组

以输入:coins = [1, 2, 5], amount = 5为例

C++代码

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 - coins[i]]是初始值则跳过dp[j] = min(dp[j], dp[j - coins[i]] + 1);}}}if (dp[amount] == INT_MAX) return -1;return dp[amount];}
};

C++测试用例有两个数相加超过int的数据,所以需要在if里加上
dp[i] < INT_MAX - dp[i - num]。


总结

动态规划
英文:Dynamic Programming,简称DP,如果某一问题有很多重叠子问题,使用动态规划是最有效的。
动态规划中每一个状态一定是由上一个状态推导出来的,这一点就区分于贪心,贪心没有状态推导,而是从局部直接选最优的

对于动态规划问题,可以拆解为如下五步曲,这五步都搞清楚了,才能说把动态规划真的掌握了!

  1. 确定dp数组(dp table)以及下标的含义
  2. 确定递推公式
  3. dp数组如何初始化
  4. 确定遍历顺序
  5. 举例推导dp数组

这篇文章主要总结了一些动态规划解决322. 零钱兑换问题,依然是使用动规五部曲,做每道动态规划题目这五步都要弄清楚才能更清楚的理解题目!

判断如何初始化重要一点就是看dp是取之前状态最小值还是最大值,如果最小值就大概率INT_MAX,最大值就大概率0。

在求装满背包有几种方案的时候,认清遍历顺序是非常关键的。

  • 如果求组合数就是外层for循环遍历物品,内层for遍历背包。(也就是0-1背包一维dp数组常用写法)

  • 如果求排列数就是外层for遍历背包,内层for循环遍历物品。

而本题是要求最少硬币数量,硬币是组合数还是排列数都无所谓!所以两个for循环先后顺序怎样都可以!

欢迎大家关注本人公众号:编程复盘与思考随笔

(关注后可以免费获得本人在csdn发布的资源源码)

LeetCode刷题复盘笔记—一文搞懂完全背包之322. 零钱兑换问题(动态规划系列第十四篇)相关推荐

  1. LeetCode刷题复盘笔记—一文搞懂0 - 1背包之494. 目标和问题(动态规划系列第九篇)

    今日主要总结一下动态规划0-1背包的一道题目,494. 目标和问题 题目:494. 目标和 Leetcode题目地址 题目描述: 给你一个整数数组 nums 和一个整数 target . 向数组中的每 ...

  2. LeetCode刷题复盘笔记—1373. 二叉搜索子树的最大键值和

    今日主要总结一下,1373. 二叉搜索子树的最大键值和 题目:1373. 二叉搜索子树的最大键值和 Leetcode题目地址 题目描述: 给你一棵以 root 为根的 二叉树 ,请你返回 任意 二叉搜 ...

  3. leetcode刷题错误笔记(树之前)

    1.简单数据结构 1.1 数组一 二分查找: 看题目,数组升序,复杂度log n,想到二分查找. class Solution {public int search(int[] nums, int t ...

  4. LeetCode刷题——最长回文子串

    目录 一.题目描述 二.题解 三.源码 一.题目描述 二.题解 三.源码 class Solution(object):def longestPalindrome(self, s):"&qu ...

  5. leetcode 322. Coin Change | 322. 零钱兑换(动态规划)

    题目 https://leetcode.com/problems/coin-change/ 题解 也许是第一次在没看答案的情况下写的动态规划- 第一反应是,这题不是广义背包吗?想了一下,不是,因为广义 ...

  6. 卷进大厂系列之LeetCode刷题笔记:二分查找(简单)

    LeetCode刷题笔记:二分查找(简单) 学算法,刷力扣,加油卷,进大厂! 题目描述 涉及算法 题目解答 学算法,刷力扣,加油卷,进大厂! 题目描述 力扣题目链接 给定一个 n 个元素有序的(升序) ...

  7. LeetCode刷题笔记汇总

    LeetCode刷题笔记汇总 第一次刷LeetCode写的一些笔记. 1.两数之和 3.无重复字符的最长子串 15.三数之和 18.四数之和 19.删除链表的倒数第 N 个结点 20.有效的括号 21 ...

  8. leetcode刷题笔记——二分查找

    leetcode刷题笔记--二分查找 目前完成的贪心相关的leetcode算法题序号: 中等:80,81 困难:4 来源:力扣(LeetCode) 链接:https://leetcode-cn.com ...

  9. LeetCode刷题笔记2——数组2

    LeetCode刷题笔记2--数组2 重塑数组 题目 在 MATLAB 中,有一个非常有用的函数 reshape ,它可以将一个 m x n 矩阵重塑为另一个大小不同(r x c)的新矩阵,但保留其原 ...

最新文章

  1. LabVIEW实现PCB电路板元器件匹配定位(实战篇—7)
  2. 用Flutter改造ZS项目小记一:界面显示一张图片
  3. junit5_使用junit做其他事情
  4. Redis集群~StackExchange.Redis(10月6号版1.1.608.0)连接Twemproxy支持Auth指令了
  5. 西门子实数转整数_西门子PLC指令全都翻译过来了!
  6. linux文件管理 - 系统目录结构
  7. 蓝桥杯大赛青少年创意编程 第十一届 省赛 C++组试题 第1题 双面打印
  8. 如何用手机NFC代替小区门禁?
  9. jenkins JDK的集成
  10. 区块链的开源资料 Hyperledger Fabric
  11. 虚拟IP技术-VIP 与 负载均衡
  12. 修改 cmd 控制台默认代码页编码
  13. (实用工具分享)网页尺寸测量工具Page Ruler
  14. 数学对数 log lg ln 简介
  15. 卷三十一 汉纪二十三
  16. 点击图片放大,实现移动端双指缩放,单指拖拽功能
  17. django 连接oracle的坑和解决方法
  18. EXCEL中的IF嵌套逻辑理解
  19. 企业项目管理数字化解决方案
  20. ARM 物联网平台安全架构(PSA)

热门文章

  1. OpenCASCADE:OCCT应用框架OCAF之可视化属性
  2. (WDM)波分复用的总结
  3. java 银行接口_农业银行接口Java-Demo-V3.1.6
  4. java常用集合浅层解析-面试必备
  5. 中国的尺和英尺的区别
  6. python 软件工程_软件工程实践之 django/python
  7. mysql客户端启动失败_MySQL Study案例之--MySQL客户端连接故障
  8. 女人必看与男人约会12个潜规则
  9. 如何增加网站的百度收录量?
  10. 查验微信封域名php,如何检测域名网址是否被微信封 微信域名检测接口的实现解析...