322. 零钱兑换
题目来源
题目分析
给定一个硬币面额数组
coins和一个总金额amount,我们需要计算凑成该总金额所需的最少硬币个数。如果无法凑成该金额,则返回-1。
题目难度
- 难度:中等
题目标签
- 标签:数组、动态规划
题目限制
1 <= coins.length <= 121 <= coins[i] <= 5000
解题思路
这道题可以使用动态规划来解决,是一个典型的完全背包问题:
-
问题定义:设
dp[i]为凑成金额i所需的最少硬币个数,目标是求出dp[amount]。 -
状态转移:对于每个硬币面额
coin,如果我们选择使用该硬币,则dp[i] = dp[i - coin] + 1。我们需要在所有可能的选择中取最小值,即dp[i] = min(dp[i], dp[i - coin] + 1)。 -
初始化:
- 初始化
dp[0] = 0,表示凑成金额0所需的硬币个数为0。 - 初始化
dp[i] = amount + 1表示初始状态下无法凑成金额i。
- 初始化
-
边界条件:
- 如果
dp[amount]仍然等于amount + 1,说明无法凑成该金额,返回-1。 - 否则,返回
dp[amount]。
- 如果
核心算法步骤
-
初始化:
- 定义
dp数组,长度为amount + 1,初始化为amount + 1,表示无法凑成该金额。
- 定义
-
状态转移:
- 对于每个
coin,遍历从coin到amount的所有金额,更新dp数组。
- 对于每个
-
输出结果:
- 返回
dp[amount],如果dp[amount]等于amount + 1,则返回-1,否则返回dp[amount]。
- 返回
代码实现
以下是求解零钱兑换问题的 Java 代码:
/**
* 322. 零钱兑换
* @param coins 硬币面额数组
* @param amount 总金额
* @return 凑成总金额所需的最少的硬币个数
*/
public int coinChange(int[] coins, int amount) {
int[] dp = new int[amount + 1];
for (int i = 1; i <= amount; i++) {
dp[i] = amount + 1;
}
for (int coin : coins) {
for (int j = coin; j <= amount; j++) {
dp[j] = Math.min(dp[j], dp[j - coin] + 1);
}
}
return dp[amount] == amount + 1 ? -1 : dp[amount];
}
代码解读
coinChange方法:- 初始化
dp数组,dp[i]表示凑成金额i所需的最少硬币个数,初始状态下设为amount + 1表示不可达。 - 外层循环遍历每个
coin,内层循环更新dp数组,尝试通过当前coin凑成不同的金额。
- 初始化
性能分析
- 时间复杂度:
O(n * amount),其中n是coins数组的长度,amount是目标金额。 - 空间复杂度:
O(amount),因为我们只需要一个长度为amount + 1的数组dp。
测试用例
你可以使用以下测试用例来验证代码的正确性:
int[] coins1 = {1, 2, 5};
int amount1 = 11;
int result1 = coinChange(coins1, amount1);
System.out.println(result1);
// 输出: 3 (11 = 5 + 5 + 1)
int[] coins2 = {2};
int amount2 = 3;
int result2 = coinChange(coins2, amount2);
System.out.println(result2);
// 输出: -1 (无法凑成3)
int[] coins3 = {1};
int amount3 = 0;
int result3 = coinChange(coins3, amount3);
System.out.println(result3);
// 输出: 0 (无需任何硬币即可凑成0)
扩展讨论
其他实现
- BFS:
- 也可以使用广度优先搜索 (BFS) 来解决零钱兑换问题,从
amount开始不断减去coins中的值,直到凑成0或无解。
- 也可以使用广度优先搜索 (BFS) 来解决零钱兑换问题,从
总结
通过动态规划的思想,我们能够高效地解决零钱兑换问题。通过合理定义 dp 数组的含义,状态转移方程,以及对初始状态的处理,可以解决类似的完全背包问题。


被折叠的 条评论
为什么被折叠?



