代码随想录算法训练营day34 day36| 卡码网46题.01背包 416.分割等和子集 1049. 最后一块石头的重量 II 494. 目标和 474.一和零
学习资料:https://programmercarl.com/背包理论基础01背包-1.html
动态规划 01背包问题
学习记录:
卡码网46题.01背包
点击查看代码
# # 二维背包解法
# n, bagweight = map(int, input().split())
# weight= list(map(int, input().split()))
# value = list(map(int, input().split()))
# dp = [[0]*(bagweight+1) for _ in range(n)]
# for j in range(weight[0], bagweight+1):
# dp[0][j] = value[0]
# for i in range(1,n):
# 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])
# print(dp[n-1][bagweight])
# 一维背包解法
n,bagweight = map(int, input().split())
weight = list(map(int, input().split()))
value = list(map(int, input().split()))
dp = [0]*(bagweight+1)
dp[0]=0
for i in range(n):
for j in range(bagweight, weight[i]-1, -1):
dp[j] = max(dp[j],dp[j-weight[i]]+value[i])
print(dp[bagweight])
416.分割等和子集
点击查看代码
class Solution(object):
def canPartition(self, nums):
"""
:type nums: List[int]
:rtype: bool
"""
# 0-1背包问题
# 就是先看数组求和后是否为偶数,不是的话不能分割等和
_sum = sum(nums)
if _sum % 2== 1:
return False
else:
target = _sum // 2
# dp是背包容量为j时的价值之和
dp = [0]*10001
dp[0]=0
for i in range(len(nums)): # 先遍历物品
for j in range(target, nums[i]-1, -1): # 再逆序遍历背包容量,只能遍历到num处
dp[j] = max(dp[j], dp[j-nums[i]]+nums[i]) # 递推公式,
if dp[target] == target: # 如果价值之和等于背包容量,满足题意
return True
return False
1049.最后一块石头的重量 II
点击查看代码
class Solution(object):
def lastStoneWeightII(self, stones):
"""
:type stones: List[int]
:rtype: int
"""
# 与分割等和子集同理,01背包,把石头尽量均分为两部分,再相互撞击就能留下最小重量的石头
_sum = sum(stones)
target = _sum // 2 # 若不能均分,则此值向下取整,对应结果的两倍小于_sum,在最后返回值时也不会超出原重量
# 初始化,背包容量为j时的最大重量,为啥1501,是因为30*100/2再大一点
dp = [0]*1501
# 先遍历每个石头
for stone in stones:
# 再逆序遍历背包,最多循环至石头质量处
for j in range(target, stone-1, -1):
dp[j] = max(dp[j], dp[j-stone]+stone) # 求j背包容量时最大重量
return _sum - dp[target] - dp[target] # 返回两组对撞后剩下的质量
494.目标和
点击查看代码
class Solution(object):
def findTargetSumWays(self, nums, target):
"""
:type nums: List[int]
:type target: int
:rtype: int
"""
# 相当于把数组分为两类,一类求和,另一类先取反再求和,即一正和、一负和
_sum = sum(nums)
# 不能通过计算获得target
if abs(target) > _sum:
return 0
if (target + _sum) % 2 == 1:
return 0
# 没看懂,感觉很巧妙
target_sum = (target + _sum) // 2
# 构造dp数组,背包容量为j时,装满有dp[j]种方法
dp = [0] * (target_sum + 1)
# 初始化不同,若数组为[0],则背包容量为0时,可以把0装进去,就一种方法
dp[0]=1
# 数字正序遍历
for num in nums:
# 和逆序遍历,已知到num停止
for j in range(target_sum, num-1, -1):
dp[j] += dp[j-num]
# 容量为target_sum的背包,装满的方法个数
return dp[target_sum]
474.一和零
点击查看代码
class Solution(object):
def findMaxForm(self, strs, m, n):
"""
:type strs: List[str]
:type m: int
:type n: int
:rtype: int
"""
# 一个新型01背包问题,这里的重量和价值都变成了(0的个数和1的个数)
dp = [[0]*(n+1) for _ in range(m+1)] # i个0和j个1的容量时,有dp[i][j]种装法
# 先正序遍历物品,并统计这个物品有几个0几个1
for s in strs:
zeroNum = s.count('0')
oneNum = len(s) - zeroNum
# 再逆序遍历背包重量,这里重量变成几个0和几个1,所以是两重循环
for i in range(m, zeroNum-1, -1):
for j in range(n, oneNum-1, -1):
dp[i][j] = max(dp[i][j], dp[i-zeroNum][j-oneNum]+1) # 装入1个物品(它有zeroNum个0,oneNum个1
return dp[m][n]
PS:终于补完了,背包问题都没听太懂,不知道构造dp数组一维时要多少个元素,不知道遍历重量或者价值时的循环内设置的起点和终点
今天天气真好,找工作有了一点点进展希望继续,今天朋友过生日吃了美味蛋糕和火锅,巴适~
浙公网安备 33010602011771号