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] <= 2
31
- 1
0 <= amount <= 10
4
法1 动态规划 二维dp
思路
经典的完全背包问题
状态定义:
dp[i][j]
表示 coin[0:i]
个硬币中组成金额为 j
的最少数目。状态转移:
dp[i][j] = min(dp[i-1][j], dp[i][j-coins[i-1]] + 1)
, 分别为不考虑当前硬币,考虑当前硬币的情况。其中没考虑当前硬币时,因为当前硬币为无限制的,所以是在dp[i] 这一层通过 牺牲一定的容量 来换取更大的面值硬币 的策略。题解
class Solution: def coinChange(self, coins: List[int], amount: int) -> int: # dp[i][j] 表示 coin[0:i] 个硬币中组成金额为 j 的最少数目 dp = [[float("inf")] * (amount+1) for _ in range(len(coins)+1)] # 初始化 for i in range(0, len(dp)): dp[i][0] = 0 for i in range(1, len(dp)): for j in range(1, amount+1): # 不考虑当前 硬币 dp[i][j] = dp[i-1][j] # 考虑当前硬币,由于硬币数目是无限制的,所以在当前层上 + 1 if j - coins[i-1] >= 0: dp[i][j] = min(dp[i][j], dp[i][j-coins[i-1]] + 1) return dp[-1][amount] if dp[-1][amount] != float('inf') else -1
优化:直接写一维 dp 也可以
class Solution: def coinChange(self, coins: List[int], amount: int) -> int: dp = [float("inf")] * (amount+1) dp[0] = 0 for i in range(1, len(coins)+1): for j in range(1, amount+1): if j - coins[i-1] >= 0: dp[j] = min(dp[j], dp[j-coins[i-1]] + 1) return dp[amount] if dp[amount] < float("inf") else -1