动态规划: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 ,表示不同面额的硬币:以及一 ...
最新文章
- 代理 设置wsl_第一次使用WSL我做了什么
- CSS使用线性渐变实现滚动进度条
- 【Linux】开源分布式存储系统:GlusterFS
- 一天搞定CSS:初识css--01
- RabbitMQ 的安装----windows环境
- python做毕业设计选题_软妹子的毕业设计:用Python制作防盗门系统!
- android home键后计时拉起app_使用React Native完成App软件
- mysql 5.1升级5.6_mysql 5.1.71升级到5.6.30
- 国庆佳节,我们有好礼相送!
- Java-Concurrent 线程池ThreadPoolExecutor使用
- leetcode python3 简单题119. Pascal's Triangle II
- Docker容器的数据卷备份与恢复
- 计算机SCI期刊征稿 | 影响因子最高10+,一区,毕业/评职称不要错过!
- 信贷违约风险预测(四)TrAiNiNG MoDeL
- 带你掌握最常用的数据分析图表
- freecommander 快捷键列表 zz
- 没有期刊申请清华博士_清华大学官方:“博士生无须发表论文”理解有误!
- 三极管---初识(图文并茂)
- 【Akka】Akka Actor生命周期
- WordPress博客系统搜索引擎优化seo全攻略方法
热门文章
- 用JDOM创建XML完整实例(包括定义xmlns:xsi和xsi:type)
- 化妆品店有哪些好用的活动方案?
- LevelDB设计与实现
- matlab-sub2ind()和ind2sub()
- Kenken Race
- 移动互联网广告模式有什么问题?
- Windows 7下通过Excel2007连接Oracle数据库并对表查询
- python 拟合曲线 置信区间_如何从曲线获得置信区间
- 小程序手动设置缓存时间
- 更换服务器 百度抓取显示有跳转,关于百度抓取诊断有跳转的问题!求各位SEO高手帮帮我!(悬赏1元) - 搜外SEO问答...