动态规划:322. 零钱兑换(完整思路过程)
题目
322. 零钱兑换
给你一个整数数组
coins
,表示不同面额的硬币;以及一个整数amount
,表示总金额。
计算并返回可以凑成总金额所需的 最少的硬币个数 。如果没有任何一种硬币组合能组成总金额,返回-1
。
你可以认为每种硬币的数量是无限的。
来源:力扣(LeetCode)
题目特征:
- 不同面额的数组:
int[] coins
;总金额:int amount
- 不同面额的硬币数量是无限的。
思路:动态规划
- 定义:
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;
}
发现这样写代码过于繁琐:
- 因为需要找出最小值,所以最开始要给
min
或者dp[i]
赋上最大值; - 在找最小值时,
dp[i-c] = -1
的项是不能参与的:dp[i-c] = -1
表示 没有任何一种硬币组合能组成总金额i-c
,自然也就不能通过(i-c) + c
的方式凑出i
了; - 最后,需要判断
min
或dp[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官方题解 中,用变量 max
将 amount + 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];
}
总结
- 将数组
dp
元素的初值赋上最大值,在最后判断dp[amount] == max
; - 记住
dp[0] = 0
。记不住的话在for
循环里单独考虑if (i == c) dp[i] = 1;
动态规划:322. 零钱兑换(完整思路过程)相关推荐
- 【必备算法】动态规划:LeetCode题(六)322. 零钱兑换,518. 零钱兑换 II
322. 零钱兑换² 给定不同面额的硬币 coins 和一个总金额 amount.编写一个函数来计算可以凑成总金额所需的最少的硬币个数.如果没有任何一种硬币组合能组成总金额,返回 -1. 示例 1: ...
- LeetCode刷题复盘笔记—一文搞懂完全背包之322. 零钱兑换问题(动态规划系列第十四篇)
今日主要总结一下动态规划完全背包的一道题目,322. 零钱兑换 题目:322. 零钱兑换 Leetcode题目地址 题目描述: 给你一个整数数组 coins ,表示不同面额的硬币:以及一个整数 amo ...
- [LeetCode] 322.零钱兑换 五种方法讲解
322.零钱兑换 五种方法讲解 文章目录 322.零钱兑换 五种方法讲解 1 问题描述 2 问题分析 3 解决策略 3.1 递归-暴力解决 3.2 递归-加入存储 3.3 BFS 3.4 动态规划-自 ...
- LeetCode:322. 零钱兑换(python)
LeetCode:322. 零钱兑换(python) 给定不同面额的硬币 coins 和一个总金额 amount.编写一个函数来计算可以凑成总金额所需的最少的硬币个数.如果没有任何一种硬币组合能组成总 ...
- LeeCode 322. 零钱兑换
322. 零钱兑换 难度中等709收藏分享切换为英文关注反馈 给定不同面额的硬币 coins 和一个总金额 amount.编写一个函数来计算可以凑成总金额所需的最少的硬币个数.如果没有任何一种硬币组合 ...
- LeetCode 322. 零钱兑换
322. 零钱兑换 难度 中等 给你一个整数数组 coins ,表示不同面额的硬币:以及一个整数 amount ,表示总金额. 计算并返回可以凑成总金额所需的 最少的硬币个数 .如果没有任何一种硬币组 ...
- golang力扣leetcode 322.零钱兑换
322.零钱兑换 322.零钱兑换 题解 代码 322.零钱兑换 322.零钱兑换 题解 //state: dp[i]金额为i时所需最少硬币个数 //function: dp[i]=dp[i-n]+1 ...
- Java实现 LeetCode 322 零钱兑换
322. 零钱兑换 给定不同面额的硬币 coins 和一个总金额 amount.编写一个函数来计算可以凑成总金额所需的最少的硬币个数.如果没有任何一种硬币组合能组成总金额,返回 -1. 示例 1: 输 ...
- leetcode: 322.零钱兑换
322.零钱兑换 来源:力扣(LeetCode) 链接: https://leetcode.cn/problems/coin-change/ 给你一个整数数组 coins ,表示不同面额的硬币:以及一 ...
最新文章
- [JS] [编程题] 配置文件恢复
- 该文件 linux命令,Linux网络系统,如果执行行命令#chmod 746 file.txt,那么该文件的权限是?...
- 九度OJ 区间问题 10000个随机正负数生成
- Laravel 5.2问题-----postman进api的post请求,为什么出现Forbidden?
- the Open Source Community
- RedmiK40系列首销5分钟破30万台 旗舰焊门员实至名归
- 【Elasticsearch】Elasticsearch 7.4的 soft-deletes 是个什么鬼
- 剑指offer-数值的整数次方
- react实现异步插件_初识react(四) react中异步解决方案之 redux-saga
- bscroll 滚动位置_BScroll左右联动导航
- k8s部署prometheus的相关配置
- java获取当天是周几
- 使用MindSpore进行一阶导数计算
- ADS1292R测量心电+呼吸
- 天玑9200实测成绩:CPU单核成绩突破1400分,堪称安卓芯皇
- 网红“小红书”,电商销售新模式
- 如何用C语言开发图形化游戏
- 色域空间 sRGB Adobe RGB Pro Photo RGB
- 行人轨迹预测ETH数据集坐标转换
- 移动端键盘弹出后,导致背景图片上移解决方法(MUI)
热门文章
- 初到上海的第一个月,社会对年轻人(自己)的鞭挞
- python制作照片_Python-制作抖音图片
- 三种近场通信技术特点以及未来应用场景分析与预测
- 怎么判断前轮左右的位置_驾驶位怎么判断前轮的位置 怎样确定汽车前面左右轮的位置?...
- PHP的依赖注入是干什么的?底层原理是什么?
- JZ54---字符流中第一个不重复的字符
- 2013年10月微软MVP当选名单揭晓
- 用模拟计算机求电路方程的解实验报告,电路计算机辅助分析实验报告..docx
- HTML5期末大作业:电影介绍类型网站设计——《盗梦空间》8页 HTML+CSS+JavaScript 学生DW网页设计作业成品 web课程设计网页规划与设计
- 如何使用开发者工具?