322.零钱兑换
给定不同面额的硬币 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
提示:
- 1 <= coins.length <= 12
- 1 <= coins[i] <= 231 - 1
- 0 <= amount <= 104
解答:
动态规划:尝试分解子问题
-我们经常听到「最优子结构」「缩小问题规模」「自顶向下」「自底向上」等跟动态规划相关的词汇。
-但是想通都很不容易。
---------------开始解题-----------
1.假设给出的不同面额的硬币是[1,2,5],目标为120,问最少需要多少个硬币。
2.这里我们要分层级找最优子结构,下面举例子;
3.我们使用「自顶向下」来考虑这个问题,然后使用「自底向上」的方法解决问题。
-dp[i]:表示总金额为i时候的最优解法的硬币数;
-我们思考目标值120有多少种解法:
1.拿面值为1的硬币 + 总额为119的最优解法的硬币数量;
即:dp[119] + 1;
2.拿面值为2的硬币 + 总额为118的最优解法的硬币数量;
即:dp[118] + 1;
3.拿面值为5的硬币 + 总额为115的最优解法的硬币数量;
即:dp[115] + 1;
-所以目标120的解法就是这三种的最优解法的一种,也就是硬币数量最少的一种。
即:dp[120] = Math.min(dp[119] + 1, dp[118] + 1, dp[115] + 1);
-我们可以推导出它的公式来
dp[i] = Math.min(dp[i - coin] + 1, dp[i - coin] + 1, ...);
-要想得到dp[120],就要先得到dp[119],dp[118],dp[115];
所以就应该从dp[0],dp[1],dp[2]往上推「自底向上」。
题解:
var coinChange = function(coins, amount) {
//dp长度为amount+1,最后一个值为dp[amount]也就是最后要求的值
let dp = new Array(amount+1).fill(Infinity);
dp[0] = 0;
for(let i=1; i<dp.length; i++) {
for(let c of coins) {
if(i - c >= 0) { //金额大于能使用的硬币面值
//自底向上算出dp[1],dp[2],dp[3]...并保存起来
//dp[11](目标值amount为11),coins为[1,2,5]
//即dp[11] = Math.min(dp[10]+1(面额1), dp[9]+1(面额2), dp[6]+1(面额5));
dp[i] = Math.min(dp[i], dp[i-c]+1);
}
}
}
//dp[amount]为初始值,表示不存在
return dp[amount] === Infinity ? -1 : dp[amount];
};