01背包: 一种物品只用一次1或者不用0
有n件物品和一个最多能背重量为w 的背包。第i件物品的重量是weight[i],得到的价值是value[i] 。每件物品只能用一次,求解将哪些物品装入背包里物品价值总和最大。’
多重背包: 一种物品用x次 x>=0
把有多件的物品摊开(*n)就化简为了01背包==有n件物品和一个最多能背重量为w 的背包。第i件物品的重量是weight[i],得到的价值是value[i] 。每件物品只能用一次,求解将哪些物品装入背包里物品价值总和最大。
def test_2_wei_bag_problem1(weight, value, bagweight):
# 二维数组
dp = [[0] * (bagweight + 1) for _ in range(len(weight))]
# 初始化
for j in range(weight[0], bagweight + 1):
dp[0][j] = value[0]
# weight数组的大小就是物品个数
for i in range(1, len(weight)): # 遍历物品
for j in range(bagweight + 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]] + value[i])
return dp[len(weight) - 1][bagweight]
if __name__ == "__main__":
weight = [1, 3, 4]
value = [15, 20, 30]
bagweight = 4
result = test_2_wei_bag_problem1(weight, value, bagweight)
print(result)
416.分割等和子集
- 对题目要转一个弯,分割等和子集就是使得其中一部分和为 总和/2
- ① 奇数肯定不可能,返回False
- ② n背包能装最大的重量为n,即可
- 初始化数组的简洁写法
class Solution:
def canPartition(self, nums: List[int]) -> bool:
N = sum(nums)
if N%2==1:
return False
N = N//2
dp =[0]*(N+1)
k = nums[0]
while(k<N+1):
dp[k] = nums[0]
k += 1
for i in range(1,len(nums)):
for j in range(N,nums[i]-1,-1):
dp[j] = max(dp[j],dp[j-nums[i]]+nums[i])
return dp[-1]==N
## 简化初始化dp
class Solution:
def canPartition(self, nums: List[int]) -> bool:
N = sum(nums)
if N%2==1:return False
N = N//2
dp =[0]*nums[0]+[nums[0]]*(N-nums[0]+1)
for i in range(1,len(nums)):
for j in range(N,nums[i]-1,-1):
dp[j] = max(dp[j],dp[j-nums[i]]+nums[i])
return dp[-1]==N
1049.最后一块石头的重量II
- 主要是要从题意中理解出来是01背包,这是有点困难的,还需多练习
class Solution:
def lastStoneWeightII(self, stones: List[int]) -> int:
N = sum(stones)//2
dp = [0]*stones[0] + [stones[0]]*(N-stones[0]+1)
for i in stones[1:]:
for j in range(N,i-1,-1): ## 注意这里的倒序要取到i,因此是i-1
dp[j] = max(dp[j],dp[j-i]+i)
return sum(stones)-2*dp[-1]
494.目标和
- 难点在将题意转化为 刚好装满背包容量n(target+sum(nums))//2的组合数量
- dp数组的含义为组合数,然后再想状态转移方程
class Solution:
def findTargetSumWays(self, nums: List[int], target: int) -> int:
N = (target+sum(nums))
if N % 2 == 1 or abs(target)>sum(nums) : return 0
N = N//2
dp = [1]+[0]*(N)
for i in nums:
for j in range(N,i-1,-1):
dp[j] += dp[j-i]
return dp[-1]
474.一和零
- 写多了之后,感觉每个题的状态转移方程搞糊涂了,还需要加强练习
class Solution:
def findMaxForm(self, strs: List[str], m: int, n: int) -> int:
def find01(s):
m0,m1 = 0,0
for i in s:
if i=='0':
m0 += 1
else:
m1 += 1
return m0,m1
dp = [[0]*(n+1) for _ in range(m+1)]
for s in strs:
m0,n1 = find01(s)
for i in range(m,m0-1,-1):
for j in range(n,n1-1,-1):
dp[i][j] = max(dp[i][j],dp[i-m0][j-n1] + 1)
return dp[-1][-1]