携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第26天,点击查看活动详情
零钱兑换
给你一个整数数组 coins ,表示不同面额的硬币;以及一个整数 amount ,表示总金额。 计算并返回可以凑成总金额所需的 最少的硬币个数 。如果没有任何一种硬币组合能组成总金额,返回 -1 。 你可以认为每种硬币的数量是无限的。
来源:力扣(LeetCode) 链接:leetcode.cn/problems/co…
分析
- 要找到凑成金额需要的最少的硬币个数,那只需要把能凑出来总金额的方法都列举出来,其中就能找到最少的硬币个数,对列举方法进行优化:如果把最少硬币个数的这种凑法当做最优子结构,再有一个状态转移方程,这道题目可以通过动态规划来解决
- 状态维护最小硬币个数,能凑成当前金额n的最小硬币个数dp[n],等于在遍历不同面额的硬币时,当前硬币面值问m,dp[n-m]+1的最小值
递归
- 使用递归循环状态,关注每个子问题,通过递归子问题得出结果
代码
/**
* @param {number[]} coins
* @param {number} amount
* @return {number}
*/
var memo = {}
var coinChange = function(coins, amount) {
if (amount === 0) return 0
if (amount < 0) return -1
if (memo[amount] !== undefined) return memo[amount]
let fewest = Number.MAX_SAFE_INTEGER
for (let c of coins) {
let sub = coinChange(coins, amount - c)
if (sub === -1) continue
fewest = Math.min(fewest, sub + 1)
}
memo[amount] = fewest === Number.MAX_SAFE_INTEGER ? -1 : fewest
return memo[amount]
}
数组迭代
- dp数组定义为总金额n最少需要dp[n]个硬币,结合常规动态规划中的关于最优子结构部分的分析,通过数组迭代,dp[n]的值就是要求的值
代码
/**
* @param {number[]} coins
* @param {number} amount
* @return {number}
*/
var coinChange = function(coins, amount) {
let dp = Array(amount + 1).fill(amount + 1);
dp[0] = 0;
for (let a = 1; a < amount + 1; a++) {
for (let c of coins) {
if (a - c >= 0) {
dp[a] = Math.min(dp[a], 1 + dp[a - c]);
}
}
}
if (dp[amount] !== amount + 1) {
return dp[amount];
} else {
return -1;
}
};
总结
- 本题的难度等级为中等
- 在得到状态转移方程之后,可以选择递归和跌打,递归遍历的模型像是树结构,迭代的模型是数组/链表
- 今天也是有收获的一天