持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第5天,点击查看活动详情
零钱兑换
题目链接:零钱兑换
题目描述:给你一个整数数组 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
解题思路
这题不能用贪心的思想来解,举个反例,coins=[1, 3, 5, 6, 7],amount=30,用贪心先用最大的面额 7,再用个 1,4 * 7 + 2 * 1 = 30,但是我们用 5 个 6 ,5 * 6 = 30 就能用最少的硬币兑换完成。
动态规划
dp[i]表示兑换面额i所需要的最少硬币,因为硬币无限,所以可以自底向上计算dp[i],对于dp[0~i]的每个状态,循环coins数组,寻找可以兑换的组合,用i面额减去当前硬币价值,dp[i-coin]再加上一个硬币数就是dp[i],最后取最小值就是答案,状态转移方程为dp[i] = Math.min(dp[i], 1 + dp[i - coins[j]])
题解
/**
* @param {number[]} coins
* @param {number} amount
* @return {number}
*/
var coinChange = function(coins, amount) {
// 初始化一个长度元素都为 amount + 1 的数组
let dp = new Array(amount + 1).fill(amount + 1);
dp[0] = 0;
for (let i = 0; i < dp.length; i++) {
for (let j = 0; j < coins.length; j++) {
if (i - coins[j] >= 0) {
dp[i] = Math.min(dp[i], 1 + dp[i - coins[j]]);
}
}
}
// 如果不是初始化的值说明兑换成功
return (dp[amount] !== amount + 1) ? dp[amount] : -1;
};
时间复杂度:,s 是兑换金额,n 是硬币数组长度,一共需要计算 s 个状态,每个状态需要遍历 n 个面额来转移状态
空间复杂度:,也就是 dp 数组的长度
总结
此篇文章记录了笔者在 leetcode 刷题时的一些思考,对于零钱兑换使用动态规划的思想来进行解题,希望此文章能够为大家提供一些帮助,如有不足欢迎大家指正。谢谢!