题目

322. 零钱兑换

给你一个整数数组 coins,表示不同面额的硬币;以及一个整数 amount ,表示总金额。
计算并返回可以凑成总金额所需的 最少的硬币个数 。如果没有任何一种硬币组合能组成总金额,返回 -1
你可以认为每种硬币的数量是无限的。
来源:力扣(LeetCode)

题目特征:
  1. 不同面额的数组:int[] coins;总金额:int amount
  2. 不同面额的硬币数量是无限的。

思路:动态规划

  • 定义:dp[i]:凑成总金额 i 需要的最少硬币数
  • 状态转移方程:
dp[i] = min (dp[i-c]) + 1  // `c` in the array `coins`

代码 1.0(Java)

public int coinChange(int[] coins, int amount) {if (amount == 0) return 0;int[] dp = new int[amount + 1];  // `dp[amount]` is validArrays.fill(dp, -1);  // 初识状态:(对应题干)没有任何一种硬币组合能组成总金额 i,则为`-1`dp[0] = 0;/** * 这步是为了后面 dp[i] = min + 1; 也能够处理 i == c 的情况。* 其实,删除这步也可以,在 for循环 中分开考虑:if (i - c == 0) dp[i] = 1;if (i - c > 0) ...*/for (int i = 1; i <= amount; i++) {  // i 直接从 1 开始 是因为 amount == 0 的情况在最开始已经考虑过了int min = Integer.MAX_VALUE;for (int c : coins) {if (i - c >= 0 && dp[i - c] != -1) min = Math.min(min, dp[i - c]);// else dp[i] 保持 -1,因为i < c <=> 总金额 i < 已有的硬币额 c)}if (min != Integer.MAX_VALUE) dp[i] = min + 1;}return dp[amount];
}
代码 1.0 主体的 for 循环:

非常好地体现了 dp[i] = min (dp[i-c]) + 1:先在 dp[i-c] 中找到最小值,然后让 dp[i] = min + 1

其实:

dp[i] = min (dp[i - c]) + 1 = min (dp[i-c] + 1)

所以我们可以省去变量 min

for (int i = 1; i <= amount; i++) {int dp[i] = Integer.MAX_VALUE;for (int c : coins) {if (i - c >= 0 && dp[i - c] != -1) dp[i] = Math.min(dp[i], dp[i - c] + 1);  // `i-c > -1` => `dp[i-c]` is valid}if (dp[i] != Integer.MAX_VALUE) dp[i] = -1;
}

发现这样写代码过于繁琐:

  1. 因为需要找出最小值,所以最开始要给 min 或者 dp[i] 赋上最大值;
  2. 在找最小值时,dp[i-c] = -1 的项是不能参与的:dp[i-c] = -1 表示 没有任何一种硬币组合能组成总金额 i-c,自然也就不能通过 (i-c) + c 的方式凑出 i 了;
  3. 最后,需要判断 mindp[i] 的情况。

如果 数组 dp 的每个元素的初值设为最大值 Integer.MAX_VALUE 可以很好的简化代码:

代码 2.0:

public int coinChange(int[] coins, int amount) {if (amount == 0) return 0;int[] dp = new int[amount+1];Arrays.fill(dp, Integer.MAX_VALUE);dp[0] = 0;for (int i = 1; i<=amount; i++) {for (int c : coins) {if (i-c >= 0) dp[i] = Math.min(dp[i], dp[i-c] + 1);//else dp[i] 保持最大值不变}}return dp[amount] == amount+1 ? -1 : dp[amount];
}

如果不想记忆 dp[0] = 0;,可以像 1.0代码 中注释中说的那样:在循环中将 if (i - c == 0) dp[i] = 1; 单独考虑:

 ...// dp[0] = 0;for (int i = 1; i <= amount; i++) {for (int c : coins) {if (i - c == 0) dp[i] = 1; // 补上。这样也更好理解。if (i - c > 0) dp[i] = Math.min(dp[i], dp[i-c] + 1);}}...

当然,最大值不一定非要暴力地设成 Integer.MAX_VALUE,最大值也可以是:amount + 1

因为:coins[i] >= 1,所以:最多需要 amount 个硬币。

由于 amount + 1在代码中出现频率较高(如:int[] dp = new int[amount+1];i <= amount 其实可以写成 i < amount + 1),所以在 LeetCode官方题解 中,用变量 maxamount + 1 存起来了,所以代码还可以写成:

public int coinChange(int[] coins, int amount) {int max = amount + 1;int[] dp = new int[max];Arrays.fill(dp, max);dp[0] = 0;for (int i = 1; i < max; i++) {for (int c : coins) {if (i-c >= 0) dp[i] = Math.min(dp[i], dp[i-c] + 1);}}return dp[amount] == max ? -1 : dp[amount];
}

最后,因为本题中 for 循环的顺序对结果没有影响,所以可以将 内外层循环交换位置并设置 i 的起始值 以减少对一些情况的判断(原循环中,当 i 小于 最小的硬币额度 时,内层循环没有必要再一一遍历硬币额度了):

public int coinChange(int[] coins, int amount) {int max = amount + 1;int[] dp = new int[max];Arrays.fill(dp, max);dp[0] = 0;for (int c : coins) {for (int i = c; i < max; i++) {if (i-c >= 0) dp[i] = Math.min(dp[i], dp[i-c] + 1);}}return dp[amount] == max ? -1 : dp[amount];
}

总结

  1. 将数组 dp 元素的初值赋上最大值,在最后判断 dp[amount] == max
  2. 记住 dp[0] = 0。记不住的话在 for 循环里单独考虑 if (i == c) dp[i] = 1;

动态规划:322. 零钱兑换(完整思路过程)相关推荐

  1. 【必备算法】动态规划:LeetCode题(六)322. 零钱兑换,518. 零钱兑换 II

    322. 零钱兑换² 给定不同面额的硬币 coins 和一个总金额 amount.编写一个函数来计算可以凑成总金额所需的最少的硬币个数.如果没有任何一种硬币组合能组成总金额,返回 -1. 示例 1: ...

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

    今日主要总结一下动态规划完全背包的一道题目,322. 零钱兑换 题目:322. 零钱兑换 Leetcode题目地址 题目描述: 给你一个整数数组 coins ,表示不同面额的硬币:以及一个整数 amo ...

  3. [LeetCode] 322.零钱兑换 五种方法讲解

    322.零钱兑换 五种方法讲解 文章目录 322.零钱兑换 五种方法讲解 1 问题描述 2 问题分析 3 解决策略 3.1 递归-暴力解决 3.2 递归-加入存储 3.3 BFS 3.4 动态规划-自 ...

  4. LeetCode:322. 零钱兑换(python)

    LeetCode:322. 零钱兑换(python) 给定不同面额的硬币 coins 和一个总金额 amount.编写一个函数来计算可以凑成总金额所需的最少的硬币个数.如果没有任何一种硬币组合能组成总 ...

  5. LeeCode 322. 零钱兑换

    322. 零钱兑换 难度中等709收藏分享切换为英文关注反馈 给定不同面额的硬币 coins 和一个总金额 amount.编写一个函数来计算可以凑成总金额所需的最少的硬币个数.如果没有任何一种硬币组合 ...

  6. LeetCode 322. 零钱兑换

    322. 零钱兑换 难度 中等 给你一个整数数组 coins ,表示不同面额的硬币:以及一个整数 amount ,表示总金额. 计算并返回可以凑成总金额所需的 最少的硬币个数 .如果没有任何一种硬币组 ...

  7. golang力扣leetcode 322.零钱兑换

    322.零钱兑换 322.零钱兑换 题解 代码 322.零钱兑换 322.零钱兑换 题解 //state: dp[i]金额为i时所需最少硬币个数 //function: dp[i]=dp[i-n]+1 ...

  8. Java实现 LeetCode 322 零钱兑换

    322. 零钱兑换 给定不同面额的硬币 coins 和一个总金额 amount.编写一个函数来计算可以凑成总金额所需的最少的硬币个数.如果没有任何一种硬币组合能组成总金额,返回 -1. 示例 1: 输 ...

  9. leetcode: 322.零钱兑换

    322.零钱兑换 来源:力扣(LeetCode) 链接: https://leetcode.cn/problems/coin-change/ 给你一个整数数组 coins ,表示不同面额的硬币:以及一 ...

最新文章

  1. [JS] [编程题] 配置文件恢复
  2. 该文件 linux命令,Linux网络系统,如果执行行命令#chmod 746 file.txt,那么该文件的权限是?...
  3. 九度OJ 区间问题 10000个随机正负数生成
  4. Laravel 5.2问题-----postman进api的post请求,为什么出现Forbidden?
  5. the Open Source Community
  6. RedmiK40系列首销5分钟破30万台 旗舰焊门员实至名归
  7. 【Elasticsearch】Elasticsearch 7.4的 soft-deletes 是个什么鬼
  8. 剑指offer-数值的整数次方
  9. react实现异步插件_初识react(四) react中异步解决方案之 redux-saga
  10. bscroll 滚动位置_BScroll左右联动导航
  11. k8s部署prometheus的相关配置
  12. java获取当天是周几
  13. 使用MindSpore进行一阶导数计算
  14. ADS1292R测量心电+呼吸
  15. 天玑9200实测成绩:CPU单核成绩突破1400分,堪称安卓芯皇
  16. 网红“小红书”,电商销售新模式
  17. 如何用C语言开发图形化游戏
  18. 色域空间 sRGB Adobe RGB Pro Photo RGB
  19. 行人轨迹预测ETH数据集坐标转换
  20. 移动端键盘弹出后,导致背景图片上移解决方法(MUI)

热门文章

  1. 初到上海的第一个月,社会对年轻人(自己)的鞭挞
  2. python制作照片_Python-制作抖音图片
  3. 三种近场通信技术特点以及未来应用场景分析与预测
  4. 怎么判断前轮左右的位置_驾驶位怎么判断前轮的位置 怎样确定汽车前面左右轮的位置?...
  5. PHP的依赖注入是干什么的?底层原理是什么?
  6. JZ54---字符流中第一个不重复的字符
  7. 2013年10月微软MVP当选名单揭晓
  8. 用模拟计算机求电路方程的解实验报告,电路计算机辅助分析实验报告..docx
  9. HTML5期末大作业:电影介绍类型网站设计——《盗梦空间》8页 HTML+CSS+JavaScript 学生DW网页设计作业成品 web课程设计网页规划与设计
  10. 如何使用开发者工具?