题目

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. 代理 设置wsl_第一次使用WSL我做了什么
  2. CSS使用线性渐变实现滚动进度条
  3. 【Linux】开源分布式存储系统:GlusterFS
  4. 一天搞定CSS:初识css--01
  5. RabbitMQ 的安装----windows环境
  6. python做毕业设计选题_软妹子的毕业设计:用Python制作防盗门系统!
  7. android home键后计时拉起app_使用React Native完成App软件
  8. mysql 5.1升级5.6_mysql 5.1.71升级到5.6.30
  9. 国庆佳节,我们有好礼相送!
  10. Java-Concurrent 线程池ThreadPoolExecutor使用
  11. leetcode python3 简单题119. Pascal's Triangle II
  12. Docker容器的数据卷备份与恢复
  13. 计算机SCI期刊征稿 | 影响因子最高10+,一区,毕业/评职称不要错过!
  14. 信贷违约风险预测(四)TrAiNiNG MoDeL
  15. 带你掌握最常用的数据分析图表
  16. freecommander 快捷键列表 zz
  17. 没有期刊申请清华博士_清华大学官方:“博士生无须发表论文”理解有误!
  18. 三极管---初识(图文并茂)
  19. 【Akka】Akka Actor生命周期
  20. WordPress博客系统搜索引擎优化seo全攻略方法

热门文章

  1. 用JDOM创建XML完整实例(包括定义xmlns:xsi和xsi:type)
  2. 化妆品店有哪些好用的活动方案?
  3. LevelDB设计与实现
  4. matlab-sub2ind()和ind2sub()
  5. Kenken Race
  6. 移动互联网广告模式有什么问题?
  7. Windows 7下通过Excel2007连接Oracle数据库并对表查询
  8. python 拟合曲线 置信区间_如何从曲线获得置信区间
  9. 小程序手动设置缓存时间
  10. 更换服务器 百度抓取显示有跳转,关于百度抓取诊断有跳转的问题!求各位SEO高手帮帮我!(悬赏1元) - 搜外SEO问答...