代码随想录Day26|动态规划2|01背包和多重背包

来源:代码随想录

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]
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值