题目
解题
解题一:记忆化搜索
在上面的递归树中,许多子问题被多次计算。例如,F(1) 被计算了 13 次。为了避免重复的计算,将每个子问题的答案存在一个哈希表中进行记忆化,如果下次还要计算这个值直接取出返回即可,保证每个子问题最多只被计算一次。
因为 coins 是 整数数组,也可以用数组来进行记忆;如果存在小数,只能用哈希表来记忆。
var coinChange = function(coins, amount) {
const memory = new Map();
memory.set(0, 0);
for (let coin of coins) memory.set(coin, 1);
return coinChangeHelper(coins, amount, memory);
};
var coinChangeHelper = function(coins, amount, memory) {
if (amount < 0) return -1; // 找不开
if (memory.has(amount) === false) {
let minCount = Infinity;
for (let i = 0; i < coins.length; i++) {
let res = coinChangeHelper(coins, amount - coins[i], memory);
// 判断 res 是否有效,-1 代表找不开
// 有效时再比较 minCount 与 res + 1
if (res !== -1) {
minCount = Math.min(minCount, res + 1);
}
}
// minCount = Infinity 代表找不开,设为 -1
memory.set(amount, minCount === Infinity ? -1 : minCount);
}
return memory.get(amount);
};
解题二:动态规划
详细解答:零钱兑换
因为 coins 是整数数组,所以计算 amount + 1 个状态即可。
// javascript
var coinChange = function(coins, amount) {
const dp = new Array(amount + 1).fill(Infinity);
dp[0] = 0;
for (let i = 1; i <= amount; i++) {
for (let coin of coins) {
// 因为 coin 是正整数:0 <= i - coin < i < amount,已经被计算过
// 如果其状态值为正无穷,代表找不开,正无穷无法更新初始值为正无穷的 dp[i]
// if (i >= coin && dp[i - coin] !== Infinity) {
if (i >= coin) {
dp[i] = Math.min(dp[i], dp[i - coin] + 1);
}
}
}
// coin 是正整数,最小取值是 1,所以 dp[amount] <= amount
// 如果不满足,则表明 dp[amount] 是 Infinity,意味着找不开
// return dp[amount] > Infinity ? -1 : dp[amount];
return dp[amount] > amount ? -1 : dp[amount];
};