Leetcode518. 零钱兑换ll

239 阅读2分钟

要求:

给定不同面额的硬币和一个总金额。写出函数来计算可以凑成总金额的硬币组合数。假设每一种面额的硬币有无限个。

示例:

输入: amount = 5, coins = [1, 2, 5]
输出: 4
解释: 有四种方式可以凑成总金额:
5=5
5=2+2+1
5=2+1+1+1
5=1+1+1+1+1

输入: amount = 3, coins = [2]
输出: 0
解释: 只用面额2的硬币不能凑成总金额3。

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

思路:

  1. 状态:两个,背包容量和可选择的物品。
  2. 选择:装进背包或不装进背包。如果不把coins[i]装进背包,那么dp[i][j]=dp[i-1][j]。如果把第i个物品装进背包,dp[i][j]=dp[i][h-coins[i-1]]。coins[i-1]表示的其实是第i个硬币。所以dp[i][j]其实是两种选择之和。
  3. dp数组的定义:dp[i][j]代表若只使用前i个物品,当背包容量为j时,有dp[i][j]种方法可以装满背包。
  4. base case:dp[0][...]=0, dp[...][0]=1;不使用硬币,无法凑出任何硬币。目标金额为0,必只有一种选择,什么都不选。
  5. 返回值:dp[N][amount],N为coins数组大小
模板:
int dp[N][amount+1]
dp[0][...]=0
dp[...][0]=1

for i in [1...N]:
    for j in [1...amount]:
        把物品i装进背包
        不把物品i放进背包
return dp[N][amount]

代码:

class Solution {
    public int change(int amount, int[] coins) {
        int n = coins.length;
        int[][] dp = new int[n+1][amount+1];
        for (int i = 0; i < n+1; i++) {
            dp[i][0] = 1;
        }
        for (int i = 1; i < n+1; i++) {
            for (int j = 1; j < amount+1; j++) {
                if (j-coins[i-1] >= 0) {//背包容量装得下
                    dp[i][j] = dp[i-1][j] + dp[i][j-coins[i-1]];
                } else {
                    dp[i][j] = dp[i-1][j];
                }
            }
        }
        return dp[n][amount];
    }
}