LeetCode 518. 零钱兑换 II
题目链接:518. 零钱兑换 II
思路:分析本题的动规五部曲:
- 确定dp数组及其下标的含义
dp[j]:凑成金额数为 j 的货币组合数为dp[j] - 确定递推公式
dp[j] 就是所有的dp[j - coins[i]]相加,所以递推公式为: d p [ j ] + = d p [ j − c o i n s [ i ] ] dp[j] += dp[j - coins[i]] dp[j]+=dp[j−coins[i]] - 初始化dp数组
dp[0] = 1,可以理解为凑成0元金额的组合数为1种 - 确定遍历顺序
回看一下dp数组的定义,是凑成金额数为 j 的组合数,也就是说从金额开始遍历数组中的每个货币,把这句话翻译一下就是,先遍历货币(物品)、再遍历金额(背包容量)。 - 打印dp数组结果
Python版本:
class Solution:
def change(self, amount: int, coins: List[int]) -> int:
dp = [0] * (amount+1)
dp[0] = 1
for i in range(len(coins)):
for j in range(coins[i], amount+1):
dp[j] += dp[j-coins[i]]
return dp[-1]
时间复杂度: O ( n ∗ a m o u n t ) O(n * amount) O(n∗amount),空间复杂度: O ( a m o u n t ) O(amount) O(amount)
go版本:
func change(amount int, coins []int) int {
dp := make([]int, amount+1)
dp[0] = 1
for i:=0; i<len(coins); i++ {
for j:=coins[i]; j<amount+1; j++ {
dp[j] += dp[j-coins[i]]
}
}
return dp[amount]
}
LeetCode 377. 组合总和 Ⅳ
题目链接:377. 组合总和 Ⅳ
思路:分析本题的动规五部曲:
- 确定dp数组及其下标的含义
dp[j]:和为 j 的组合数为dp[j] - 确定递推公式
dp[j] 就是所有的dp[j - nums[i]]相加,所以递推公式为: d p [ j ] + = d p [ j − n u m s [ i ] ] dp[j] += dp[j - nums[i]] dp[j]+=dp[j−nums[i]] - 初始化dp数组
dp[0] = 1,可以理解为和为0的组合数为1种 - 确定遍历顺序
回看一下dp数组的定义,是和为 j 的组合数,而这道题与上一题的区别就在于:上一题排列不强调顺序,而本题不同的排列顺序是另一种组合。所以本题要先遍历背包容量,在内循环里遍历物品。
我们总结出一个规律:如果题目的排列不强调顺序,那应该先遍历物品,再遍历背包容量;如果题目的排列强调顺序(不同的排列顺序表示不同的结果),那应该先遍历背包容量,再遍历物品。 - 打印dp数组结果
Python版本:
class Solution:
def combinationSum4(self, nums: List[int], target: int) -> int:
dp = [0]*(target+1)
dp[0] = 1
for i in range(1, target+1):
for j in range(len(nums)):
if i-nums[j]>=0:
dp[i] += dp[i-nums[j]]
return dp[target]
时间复杂度: O ( n ∗ t a r g e t ) O(n * target) O(n∗target),空间复杂度: O ( t a r g e t ) O(target) O(target)
go版本:
func combinationSum4(nums []int, target int) int {
dp := make([]int, target+1)
dp[0] = 1
for i:=1; i<=target; i++ {
for j:=0; j<len(nums); j++ {
if i-nums[j]>=0 {
dp[i] += dp[i-nums[j]]
}
}
}
return dp[target]
}