赞
踩
416. 分割等和子集
题意:01背包,共i个物品能否装满容量为j的背包
dp[j]表示在前i个物品中任取,装入容量为j的背包的最大价值,重量与价值大小相同
dp[0]=0,背包容量最大为物品重量总和的一半,最小为第i个物品的重量
class Solution:
def canPartition(self, nums: List[int]) -> bool:
total_sum = sum(nums)
if total_sum%2: return False
dp = [0]*(total_sum//2+1)
for i in range(len(nums)):
for j in range(total_sum//2,nums[i]-1,-1):
dp[j] = max(dp[j],dp[j-nums[i]]+nums[i])
if dp[-1]==total_sum//2: return True
return False
1049.最后一块石头的重量II
题意:将石头分为尽可能均等的两份,求差
即,尽可能装满sum()//2的背包,重量=价值
返回值为sum-dp[-1]-dp[-1]
class Solution:
def lastStoneWeightII(self, stones: List[int]) -> int:
total_sum = sum(stones)
dp = [0]*(total_sum//2+1)
for i in range(len(stones)):
for j in range(total_sum//2,stones[i]-1,-1):
dp[j] = max(dp[j], dp[j-stones[i]]+stones[i])
return total_sum-dp[-1]-dp[-1]
494.目标和
题意:分两份,一份正值一份负值,装满正值的背包有多少种方法
left-right=target,left+right=sum -> left=(sum+target)/2 正值
dp[j]表示装满容量为j的背包有多少种方法
dp[j] += dp[j-num]
dp[0] = 1
class Solution:
def findTargetSumWays(self, nums: List[int], target: int) -> int:
total_sum = sum(nums)
if abs(target)>total_sum: return False
if (total_sum+target)%2: return False
left = (total_sum+target)//2
dp = [0]*(left+1)
dp[0] = 1
for i in nums:
for j in range(left,i-1,-1):
dp[j] += dp[j-i]
return dp[-1]
474.一和零
两个维度的01背包
价值为1
dp[i][j]表示背包容量为i+j时的最大价值
class Solution:
def findMaxForm(self, strs: List[str], m: int, n: int) -> int:
dp = [[0] * (n + 1) for _ in range(m + 1)]
x = [0]*len(strs)
y = [0]*len(strs)
for i in range(len(strs)):
x[i] = strs[i].count('0')
y[i] = len(strs[i])-x[i]
for ii in range(len(strs)):
for i in range(m,x[ii]-1,-1):
for j in range(n,y[ii]-1,-1):
dp[i][j] = max(dp[i][j], dp[i-x[ii]][j-y[ii]]+1)
return dp[-1][-1]
完全背包问题:
题意:n种物品放进容量为m的包里,获得最大价值,物品取用次数不限
二维dp数组解法:
- - - - 01背包为倒序遍历,完全背包为正序遍历
- - - - 先物品后背包为组合,先背包后物品为顺序
一维dp数组解法:
- - - - dp[i][j]只与上一层的dp[i-1][j]有关,所以重复利用一行数组即可
- - - - j是 [物品[i],背包最大容量+1]
- - - - dp[j] += dp[j-coins[i]]
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]
377. 组合总和 Ⅳ
非组合,有顺序
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]]
print(dp)
return dp[-1]
322. 零钱兑换
背包容量为[0,amount+1)
背包容量为0时最少需要0个物品
与顺序无关,所以先物品再背包
物品的范围为[0,物品种类+1),背包范围为[物品[i],amount+1)
dp[j] = min(dp[j],dp[j-物品[i]]+1)
class Solution:
def coinChange(self, coins: List[int], amount: int) -> int:
dp = [sys.maxsize]*(amount+1)
dp[0] = 0
for i in range(0,len(coins)):
for j in range(coins[i],amount+1):
if dp[j-coins[i]] != sys.maxsize:
dp[j] = min(dp[j],dp[j-coins[i]]+1)
if dp[-1]==sys.maxsize: return -1
return(dp[-1])
279.完全平方数
与上题相似
class Solution:
def numSquares(self, n: int) -> int:
dp = [sys.maxsize]*(n+1)
dp[0] = 0
for i in range(1,n+1):
for j in range(i*i,n+1):
if dp[j-i*i]!=sys.maxsize:
dp[j] = min(dp[j],dp[j-i*i]+1)
if dp[-1]==sys.maxsize: return -1
return dp[-1]
139.单词拆分
背包容量为[0,len(s)+1)
物品集改为set,好找
dp[i]表示到长度i为止的字符串s是否符合要求
if([j, i] 这个区间的子串出现在字典里 && dp[j]是true) 那么 dp[i] = true
dp[0]=True
有顺序,先遍历背包
class Solution:
def wordBreak(self, s: str, wordDict: List[str]) -> bool:
wordDict = set(wordDict)
dp = [False]*(len(s)+1)
dp[0] = True
for i in range(1,len(s)+1):
for j in range(i):
if dp[j] and s[j:i] in wordDict:
dp[i] = True
break
return dp[-1]
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。