322. 零钱兑换
大约 2 分钟
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🚀 最优解:动态规划(完全背包)
这是典型的 完全背包问题(每个硬币可以无限使用)。
我们定义:
dp[i] = 凑成金额 i 所需的最少硬币数初始化:
dp[0] = 0(凑 0 元不需要硬币)- 其他
dp[i] = amount + 1(设为不可能大的值)
状态转移:
for 每个金额 i:
for 每个硬币 coin:
if i >= coin:
dp[i] = min(dp[i], dp[i - coin] + 1)最终答案:
dp[amount] == amount + 1 ? -1 : dp[amount]✅ 完整代码(Java)
import java.util.Scanner;
import java.util.Arrays;
public class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
// 读取硬币面额数组
int n = scanner.nextInt();
int[] coins = new int[n];
for (int i = 0; i < n; i++) {
coins[i] = scanner.nextInt();
}
// 读取目标金额
int amount = scanner.nextInt();
Solution solution = new Solution();
int result = solution.coinChange(coins, amount);
System.out.println(result);
scanner.close();
}
}
class Solution {
public int coinChange(int[] coins, int amount) {
int[] dp = new int[amount + 1];
Arrays.fill(dp, amount + 1);
dp[0] = 0;
for(int i = 1; i <= amount; i++){
for(int num : coins){
if(num <= i){
dp[i] = Math.min(dp[i], dp[i - num] + 1);
}
}
}
return dp[amount] == amount + 1 ? -1 : dp[amount];
}
}🧠 通俗易懂解释
你可以把这个题想象成:
“用最少数量的硬币刚好凑出金额”。
我们从 0 元开始,逐步推算到目标金额。
比如 amount = 11,coins = [1,2,5]:
dp[0] = 0算
dp[1]:只能用 1 →dp[1] = 1算
dp[2]:- 用 1 → dp[1]+1 = 2
- 用 2 → dp[0]+1 = 1(更优)
- →
dp[2] = 1
…
算
dp[11]:- 用 5 →
dp[6] + 1 - 用 2 →
dp[9] + 1 - 用 1 →
dp[10] + 1
- 用 5 →
最终求得最小值为 3(5 + 5 + 1)。
动态规划就是从小金额递推到大金额,逐渐逼近答案。
⏱ 时间复杂度
O(amount × n)
n = 硬币面额数量。
因为对于每个金额,都遍历所有硬币。
💾 空间复杂度
O(amount)
只用一个一维 dp 数组。
dp[i] 当前i的最少兑换次数
