题目
给你一个整数数组 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
所需的最少硬币个数。对于每个金额 i
,遍历所有硬币面额 coin
,如果 i >= coin
,则可以尝试使用该硬币来更新 dp[i]
。状态转移方程为:
动态规划
初始化dp
数组:创建一个长度为 amount + 1
的数组 dp
,并将所有元素初始化为 amount + 1
,因为凑成金额 i
最多需要 i
个面额为 1 的硬币,所以 amount + 1
是一个不可能达到的上限。dp[0]
初始化为 0,表示凑成金额 0 不需要任何硬币。
状态转移:遍历每个金额 i
从 1 到 amount
,对于每个金额 i
,遍历所有硬币面额 coin
,如果 i >= coin
,则更新 dp[i]
为 dp[i]
和 dp[i - coin]+1
中的较小值。
结果判断:如果 dp[amount]
仍然是 amount + 1
,说明无法凑成该金额,返回 -1;否则返回dp[amount]
。
时间复杂度:O(),
是硬币的种类数
空间复杂度:O()
class Solution {
public:
int coinChange(std::vector<int>& coins, int amount) {
// 创建 dp 数组,初始化为 amount + 1,用于后续比较更新
std::vector<int> dp(amount + 1, amount + 1);
// 凑成金额 0 所需的硬币个数为 0
dp[0] = 0;
// 遍历每个金额
for (int i = 1; i <= amount; ++i) {
// 遍历每个硬币面额
for (int coin : coins) {
if (i >= coin) {
// 更新 dp[i] 为更小的值
dp[i] = std::min(dp[i], dp[i - coin] + 1);
}
}
}
// 如果 dp[amount] 仍然是 amount + 1,说明无法凑成该金额
return dp[amount] > amount ? -1 : dp[amount];
}
};