代码随想录算法训练营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数组一维时要多少个元素,不知道遍历重量或者价值时的循环内设置的起点和终点
今天天气真好,找工作有了一点点进展希望继续,今天朋友过生日吃了美味蛋糕和火锅,巴适~

posted @ 2024-11-04 22:48  Tristan241001  阅读(13)  评论(0)    收藏  举报