给你一个整数数组
coins
,表示不同面额的硬币;以及一个整数amount
,表示总金额。计算并返回可以凑成总金额所需的 最少的硬币个数 。如果没有任何一种硬币组合能组成总金额,返回
-1
。你可以认为每种硬币的数量是无限的。
解法1 记忆化递归
思路
这题要求最少得硬币个数,会想当然的去用贪心,我从最大的开始减,然后剩下的去递归计算。这看起来直觉上很对,但实际上这是错的策略,因为它不能保证一定得出最优解。
比如 conins = [1, 3, 4], amount = 6
,如果从最大的开始减会得到 4 + 1 + 1 = 3
的答案,而不会得到 3 + 3 = 2
的答案。使用大的硬币这只在某些特定硬币组合下成立。
代码
function coinChange(coins: number[], amount: number): number {
const memo = new Map();
const dfs = (remain) => {
if (remain === 0) return 0;
if (remain < 0) return -1;
if (memo.has(remain)) return memo.get(remain);
let min = Infinity;
for (let coin of coins) {
const res = dfs(remain - coin);
if (res >= 0) {
min = Math.min(min, res + 1);
}
}
const result = (min === Infinity) ? -1 : min;
memo.set(remain, result);
return result;
};
return dfs(amount);
};
时空复杂度
时间复杂度:O(amount * n)
空间复杂度:O(amount)
解法2 动态规划
思路
dp
数组表示的就是 amount
为 i
时的最少硬币,需要从底向上开始计算。
代码
function coinChange(coins: number[], amount: number): number {
const dp = new Array(amount + 1).fill(Infinity);
dp[0] = 0;
for (let i = 1; i <= amount; i++) {
for (let coin of coins) {
if (i - coin >= 0 && dp[i - coin] !== Infinity) {
dp[i] = Math.min(dp[i], dp[i - coin] + 1);
}
}
}
return dp[amount] === Infinity ? -1 : dp[amount];
};
时空复杂度
时间复杂度:O(amount * n)
空间复杂度:O(amount)