给你一个整数数组 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
示例 4:
输入:coins = [1], amount = 1输出:1
示例 5:
输入:coins = [1], amount = 2输出:2
f(x) 表示如何利用最少的硬币组成 x 元f(11)f(10)f(9)f(6)利用 f(x) 表示最后一步的三个选项:
f(10) + 1 个 1 元得到 f(11)f(9) + 1 个 2 元得到 f(11)f(6) + 1 个 5 元得到 f(11)f(11) = min(f(11 - 1), f(11 - 2), f(11 - 5)) + 1;// 第一次替换f(x) = min(f(x - 1), f(x - 2), f(x - 5)) + 1;// 第二次替换f(x) = min(f(x - y), y in conins) + 1;// 伪代码for (let i = 0; i < conins.length; i++) {f(x) = min(f(x), f(x - i) + 1);}
f(x) 的表达直接把 f(x) 当成一个哈希函数。
dp[] 表示 f(x)dp[][] 表示 f(x, y)在此题中 f(x) 中的 x 是一个整数,f(x) 要表达的信息是一维信息。
从问题的起始输入开始调用这个递归函数,如果递归函数出现 不正确/无法计算/越界 的情况,那么这就是你需要处理的初始条件和边界。
情况:
coinChange(0):可以发现给定 0 元时,dp[amount-x] 会导致数组越界,需要特别处理 dp[0]coinChange(-1) 或者 coinChange(-2) 的调用也会遇到数组越界,说明这些情况都需要做特别处理处理技巧:
dp[0]、dp[1]dp[-1]初始条件: dp[0] = 0
dp[1] = dp[0] + 1 元硬币 = 1dp[2] = dp[0] + 2 元硬币 = 1dp[5] = dp[0] + 5 元硬币 = 1var coinChange = function (coins, amount) {// dp 数组的定义:当目标金额为 i 时,至少需要 dp[i] 枚硬币凑出let dp = new Array(amount + 1).fill(Infinity);dp[0] = 0;// 外层循环遍历所有状态的所有取值for (let i = 0; i < dp.length; i++) {// 内层循环求出所有选择的最小值for (let coin of conins) {// 子问题无解,跳过if (i - coin < 0) continue;// dp[i - coin] 获取上个子问题的解dp[i] = Math.min(dp[i], 1 + dp[i - coin]);}}return dp[amount] === Infinity ? -1 : dp[amount];};
复杂度分析:
当求最小值的时,往往将不可能的情况设置为
Number.MAX_VALUE
var coinChange = function (coins, amount) {let dp = new Array(coins.length + 1).fill().map((v) => new Array(amount + 1).fill());// 初始化行for (let i = 1; i < amount + 1; i++) {dp[0][i] = Infinity;}// 初始化列for (let j = 1; j < coins.length + 1; j++) {dp[j][0] = 0;}// 开始填表for (let i = 1; i < coins.length + 1; i++) {for (var j = 1; j < amount + 1; j++) {if (coins[i - 1] > j) {dp[i][j] = dp[i - 1][j];} else {dp[i][j] = Math.min(dp[i - 1][j], dp[i][j - coins[i - 1]] + 1);}}}return dp[coins.length][amount] === Infinity ? -1 : dp[coins.length][amount];};