零钱兑换 II
难度:
标签:
题目描述
给你一个整数数组 coins
表示不同面额的硬币,另给一个整数 amount
表示总金额。
请你计算并返回可以凑成总金额的硬币组合数。如果任何硬币组合都无法凑出总金额,返回 0
。
假设每一种面额的硬币有无限个。
题目数据保证结果符合 32 位带符号整数。
示例 1:
输入:amount = 5, coins = [1, 2, 5] 输出:4 解释:有四种方式可以凑成总金额: 5=5 5=2+2+1 5=2+1+1+1 5=1+1+1+1+1
示例 2:
输入:amount = 3, coins = [2] 输出:0 解释:只用面额 2 的硬币不能凑成总金额 3 。
示例 3:
输入:amount = 10, coins = [10] 输出:1
提示:
1 <= coins.length <= 300
1 <= coins[i] <= 5000
coins
中的所有值 互不相同0 <= amount <= 5000
代码结果
运行时间: 58 ms, 内存: 16.2 MB
/*
* Problem Statement: Given an array of integers coins representing different denominations and an integer amount representing a total amount of money, return the number of combinations that make up that amount. If that amount cannot be reached, return 0. The problem guarantees that the result fits in a 32-bit signed integer.
*
* Approach: This solution also uses dynamic programming but leverages Java Streams for some operations. We create a dp array similar to the previous solution, but this time we use streams to manipulate the data.
*/
import java.util.Arrays;
import java.util.stream.IntStream;
public class CoinChange2 {
public int change(int amount, int[] coins) {
int[] dp = new int[amount + 1];
dp[0] = 1; // Base case: one way to make amount 0
Arrays.stream(coins).forEach(coin ->
IntStream.range(coin, amount + 1).forEach(i -> dp[i] += dp[i - coin])
);
return dp[amount];
}
}
解释
方法:
这是一道典型的动态规划问题。我们定义一个一维数组 dp,其中 dp[i] 表示组成金额 i 的硬币组合数。初始状态下,除了 dp[0] 为 1(表示金额为 0 的组合数为 1)外,其余都为 0。我们遍历每一种硬币,对于每个硬币 c,我们从 c 开始更新 dp 数组,即对于每个大于等于 c 的 i,更新 dp[i] += dp[i - c]。这样做的意义在于,对于当前的硬币 c,它可以与组成金额 i - c 的所有组合组合在一起,形成金额 i 的组合。
时间复杂度:
O(n * amount)
空间复杂度:
O(amount)
代码细节讲解
🦆
在动态规划解法中,为什么选择将 dp[0] 初始化为 1,而不是其他值?
▷🦆
由于硬币数组在动态规划之前进行了逆序排序,这种排序对最终结果有什么影响?是否会影响算法的效率?
▷🦆
在更新 dp 数组的过程中,为什么从金额 c 开始更新而不是从 0 开始?这样做有什么优势?
▷🦆
该题解中如果 coins 数组包含重复值,这会对算法的输出或性能产生怎样的影响?
▷