一、01背包问题基础:
有n件物品和一个最多能背重量为w的背包。第i件物品的重量是weight[i],得到的价值是value[i]。每件物品只能用一次,求解哪些物品装入背包里物品价值总和最大。
1.用dp的方法做:
①dp[i][j]的含义:从下标为[0-i]的物品里任意取,放进容量为j的背包,价值总和最大是多少。
那么可以有两个方向推出来dp[i][j]。
②递推公式:
不放物品:此时dp[i][j]=dp[i-1][j]
放物品:此时dp[i-1][j-weight[i]+value[i]
so:dp[i][j] = max(dp[i-1][j],dp[i-1][j-wight[i]+value[i])
③初始化:dp[i][0]=0 for i in range(len(weight)(容量为0,装不了东西)
dp[0][j]==value[0] if j>=weight[0]
③如果想验证结果:建议自己画一下图。
2.用滚动数组的方法做(把二维dp降为一维dp):
①状态转移方程:dp[j] = max(dp[j],dp[j - weight[i]] + value[i])
②j需要倒序遍历
二、刷题:
1.卡码网46. 46. 携带研究材料(第六期模拟笔试) (kamacoder.com)
解决:
①二维dp方式:(ACM模式代码)
a = input().strip().split()
M = int(a[0])
N = int(a[1])
weight = [int(n) for n in input().split()]
values = [int(n) for n in input().split()]
dp =[[0]*(N + 1) for _ in range(M)]
for j in range(weight[0],N+1):
dp[0][j] = values[0]
for i in range(1,M):
for j in range(1,N+1):
if j<weight[i]:
dp[i][j] = dp[i-1][j]
else:
dp[i][j] = max(dp[i-1][j],dp[i-1][j-weight[i]] + values[i])
print(dp[M-1][N])
②降为一维dp:
(这种方法我其实不是很理解)
a = input().strip().split()
M = int(a[0])
N = int(a[1])
weight = [int(n) for n in input().split()]
values = [int(n) for n in input().split()]
dp =[0]*(N + 1)
for i in range(M):
for j in range(N,weight[i] - 1,-1):
dp[j] = max(dp[j],dp[j-weight[i]] + values[i])
print(dp[N])
2.leetcode题目416 416. 分割等和子集 - 力扣(LeetCode)
解决:
class Solution:
def canPartition(self, nums: List[int]) -> bool:
if sum(nums)%2 !=0:
return False
target = sum(nums)//2
dp = [0]*(target+1)
for num in nums:
for j in range(target,num-1,-1):
dp[j] = max(dp[j],dp[j-num]+num)
return dp[target] == target