322.零钱兑换

题目

给你一个整数数组 coins ,表示不同面额的硬币;以及一个整数 amount ,表示总金额。

计算并返回可以凑成总金额所需的 最少的硬币个数 。如果没有任何一种硬币组合能组成总金额,返回 -1 。

你可以认为每种硬币的数量是无限的。

示例 1:

输入:coins = [1, 2, 5], amount = 11
输出:3 
解释:11 = 5 + 5 + 1

示例 2:

输入:coins = [2], amount = 3
输出:-1

示例 3:

输入:coins = [1], amount = 0
输出:0

示例 4:

输入:coins = [1], amount = 1
输出:1

示例 5:

输入:coins = [1], amount = 2
输出:2

提示:

1 <= coins.length <= 12
1 <= coins[i] <= 231 - 1
0 <= amount <= 104
通过次数304,786提交次数6

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/coin-change
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

题解

从一个数组中按一定的选取方式得到目标target,尝试将题目进行转化为背包问题。
数组中的元素可以重复选择,那么是一道完全背包问题。
背包的容量为amount,物品为coins[i],物品的重量为coins[i]

1.确定dp[j]数组及下标的含义
dp[j]表示凑齐背包j所需的最少的硬币个数为dp[j]

2.确定递推式
dp[j]可以如何推出?
当前物品为coins[i]

  • 凑齐j-coins[i]的背包最少需要dp[j-coin[i]]个硬币;那么加上coins[i],凑齐j的背包需要dp[j-coin[i]]+1枚硬币
  • 不放入当前coins[i],之前已经凑齐了j的背包需要dp[j]枚硬币

本题求最少的硬币数,那么dp[j]=Math.min(dp[j],dp[j-coin[i]]+1)

3.dp数组如何初始化
选择的时是最小的硬币数,i=0时
dp[0] = 0;
amount=0时,不需要硬币。
i≠0时,dp[i] = Integer.MAX_VALUE

4.确定遍历的顺序
这道题需要的是最少的硬币个数,且是一个完全背包的问题。
先从左到右遍历物品,再从左到右遍历背包。

for(int coin:coins){
  for(int j=coin;j<=amount;j++){
    dp[j]=Math.min(dp[j],dp[j-coin]+1);
  }
}

5.举例推导dp数组
coins = [1, 2, 3], amount = 4
322.零钱兑换_动态规划

代码

class Solution {
    public int coinChange(int[] coins, int amount) {
        int [] dp = new int [amount+1];
        dp[0] = 0;
        //初始化为最大值
        for(int i = 1;i<=amount;i++){
            dp[i] = Integer.MAX_VALUE;
        }
        for(int coin:coins){
             for(int j=coin;j<=amount;j++){
                  //当dp[j-coin]为初始化时,+1会溢出变成-xxxx,也就是最小值。它为初始值说明dp[j]只有一种选择方法,就是不放入coins[i]
                  if(dp[j-coin]!=Integer.MAX_VALUE){
                      dp[j]=Math.min(dp[j],dp[j-coin]+1);
                  }
                
             }
        }
        return   dp[amount] == Integer.MAX_VALUE? -1:dp[amount];
    }
}