当前位置:   article > 正文

动态规划篇-05:零钱兑换_动态规划 零钱兑换

动态规划 零钱兑换

322、零钱兑换

我们来继续使用我们的框架。

状态转移方程

base case

当总金额数为0的时候,所需硬币数为0.

明确状态

“原问题和子问题中会变化的变量”

根据题意,问题中的变量就是”最少硬币数“。

确定选择

“导致状态变化的行为”

在这里就是”选择不同面额的硬币“

定义dp函数

一般来说,都是题中问什么,dp函数设成什么。在这里定义dp函数为:

dp(n) : 凑成总金额 n 所需的硬币数。


接下来我们就可以写出状态转移方程,同时记得使用 [分解问题] 的思想。

dp(n) = dp(n - i) + 1 // 凑成金额n所需硬币数 =  凑成金额(n-i)所需硬币数 + 选用的面值为i的硬币

在实际使用中,因为做选择时是从硬币集合中做选择,所以要将硬币集合也作为一个参数。 


暴力递归

  1. class Solution {
  2. public int coinChange(int[] coins, int amount) {
  3. return dp(coins, amount);
  4. }
  5. //dp函数返回值的定义:凑出金额amount的硬币数
  6. public int dp(int[] coins, int amount){
  7. //base case
  8. if(amount == 0){
  9. return 0;
  10. }
  11. if(amount < 0){
  12. return -1;
  13. }
  14. int res = Integer.MAX_VALUE;
  15. //遍历选择列表做选择
  16. for(int coin : coins){
  17. int subProblem = dp(coins,amount - coin);
  18. if(subProblem == -1){
  19. continue;
  20. }
  21. //寻找最优解
  22. res = Math.min(res,subProblem + 1);
  23. }
  24. return res == Integer.MAX_VALUE ? -1 : res;
  25. }
  26. }

使用备忘录的从上到下的递归解法

  1. class Solution {
  2. int[] memo;
  3. public int coinChange(int[] coins, int amount) {
  4. memo = new int[amount + 1];
  5. Arrays.fill(memo,-666);
  6. return dp(coins,amount);
  7. }
  8. int dp(int[] coins, int amount){
  9. /*使用备忘录数组后,base case 出现了新的条件*/
  10. if (amount == 0) return 0;
  11. if (amount < 0) return -1;
  12. if (memo[amount] != -666){
  13. return memo[amount];
  14. }
  15. /*接下来的判定照旧*/
  16. int res = Integer.MAX_VALUE;
  17. for(int coin : coins){
  18. int subProblem = dp(coins, amount - coin);
  19. if (subProblem == -1) continue;
  20. res = Math.min(res,subProblem + 1);
  21. }
  22. /*将计算结果存入备忘录*/
  23. memo[amount] = (res == Integer.MAX_VALUE) ? -1 : res;
  24. return memo[amount];
  25. }
  26. }

使用dp数组的自下而上的迭代解法

  1. class Solution {
  2. int coinChange(int[] coins, int amount) {
  3. int[] dp = new int[amount + 1];
  4. Arrays.fill(dp, amount + 1);
  5. // base case
  6. dp[0] = 0;
  7. // 外层 for 循环在遍历所有状态的所有取值
  8. /*外层循环什么意思?是金额!
  9. dp数组的下标是金额,dp数组的元素是硬币数*/
  10. for (int i = 0; i < dp.length; i++) {
  11. // 内层 for 循环在求所有选择的最小值
  12. for (int coin : coins) {
  13. // 子问题无解,跳过
  14. if (i - coin < 0) {
  15. continue;
  16. }
  17. dp[i] = Math.min(dp[i], 1 + dp[i - coin]);
  18. }
  19. }
  20. return (dp[amount] == amount + 1) ? -1 : dp[amount];
  21. }
  22. }

问题1:为什么数组初始化为“amount + 1”?

为啥 dp 数组中的值都初始化为 amount + 1 呢,因为凑成 amount 金额的硬币数最多只可能等于 amount(全用 1 元面值的硬币),所以初始化为 amount + 1 就相当于初始化为正无穷,便于后续取最小值

为啥不直接初始化为 int 型的最大值 Integer.MAX_VALUE 呢? 因为后面有 dp[i - coin] + 1,这就会导致整型溢出。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/不正经/article/detail/243816
推荐阅读
相关标签
  

闽ICP备14008679号