背包问题是选和不选思想的代表
0-1背包代码:
例题1
给你一个非负整数数组 nums
和一个整数 target
。
向数组中的每个整数前添加 '+'
或 '-'
,然后串联起所有整数,可以构造一个 表达式 :
- 例如,
nums = [2, 1]
,可以在2
之前添加'+'
,在1
之前添加'-'
,然后串联起来得到表达式"+2-1"
。
返回可以通过上述方法构造的、运算结果等于 target
的不同 表达式 的数目。
示例 1:
输入:nums = [1,1,1,1,1], target = 3 输出:5 解释:一共有 5 种方法让最终目标和为 3 。 -1 + 1 + 1 + 1 + 1 = 3 +1 - 1 + 1 + 1 + 1 = 3 +1 + 1 - 1 + 1 + 1 = 3 +1 + 1 + 1 - 1 + 1 = 3 +1 + 1 + 1 + 1 - 1 = 3 示例 2:输入:nums = [1], target = 1 输出:1
思想:添加正数和为p 负数-(s-p)target=p-(s-p) 所以 p=(s+target)/2
因此问题转变成从number选择一些数字,使得他们和等于(s+target)/2
问:dfs(i - 1, c) + dfs(i - 1, c - nums[i]) 中的加法是什么意思?
答:这叫加法原理,如果事件 A 和事件 B 是互斥的(即不能同时发生,不选 nums[i][的同时,又选了 nums【i】,那么发生事件 A 或事件 B 的总数等于事件 A 的数量加上事件 B 的数量。
总结:至多装capacity,就把if c==0的判断删除
至少装capacity,改成if c <=0.另外if c<nums[i]的判断也要删除
所以当题目要求恰好装capacity时,就是求方案数,用加法原理
class Solution:
def findTargetSumWays(self, nums: List[int], target: int) -> int:
target+=sum(nums)
if target<0 or target%2:
return 0
target//=2
n=len(nums)
@cache
def dfs(i,c):
if i<0:
return 1 if c==0 else 0
if c<nums[i]:
return dfs(i-1,c)
return dfs(i-1,c)+dfs(i-1,c-nums[i])
return dfs(n-1,target)
完全背包
给你一个整数数组 coins
,表示不同面额的硬币;以及一个整数 amount
,表示总金额。
计算并返回可以凑成总金额所需的 最少的硬币个数 。如果没有任何一种硬币组合能组成总金额,返回 -1
。
你可以认为每种硬币的数量是无限的。
示例 1:
输入:coins =[1, 2, 5]
, amount =11
输出:3
解释:11 = 5 + 5 + 1
class Solution:
def coinChange(self, coins: List[int], amount: int) -> int:
n=len(coins)
@cache
def dfs(i,c):
if i<0:
return 0 if c==0 else inf
if c<coins[i]:
return dfs(i-1,c)
return min(dfs(i-1,c),dfs(i,c-coins[i])+1)
ans=dfs(n-1,amount)
return ans if ans<inf else -1
分析:+1表示选了这个硬币,所以硬币个数加一,我们要求最少的,所以是min,如果dfs(i-1,c),dfs(i,c-coins[i])+1都不满足,返回值都是inf,因此输出前要判断ans的值是不是inf,是的话表示两种都不满足,返回-1