leetcode 动态规划——背包问题系列

 0-1背包问题——二维dp解决方案

import sys
input = sys.stdin.read
data = input().split()
index = 0
M = int(data[index])
index += 1
N= int(data[index])
index += 1
weight = []
value = []

for i in range(M):
    weight.append(int(data[index+i]))
index += M
for i in range(M):
    value.append(int(data[index+i]))    
#M * (N+1) ___  求得dp[M-1][N]
#dp[i][j]代表任选【0-i】的物品放入容量为j的背包里所得的最大价值
#dp[i][j] = max(dp[i-1][j], dp[i-1][j-weight[i]] + value[i])
dp = [[0] * (N + 1) for _ in range(M)]

#初始化(都是需要左上已知状态)
for j in range(N+1):
    if j >= weight[0]:
        dp[0][j] = value[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]] + value[i])
print(dp[M-1][N])

0-1背包问题——一维dp解决方案

其实就是复用每一行,细节在注释里

import sys
input = sys.stdin.read
data = input().split()
index = 0
M = int(data[index])
index += 1
N= int(data[index])
index += 1
weight = []
value = []

for i in range(M):
    weight.append(int(data[index+i]))
index += M
for i in range(M):
    value.append(int(data[index+i]))    
#由d[i][j] = max(d[i-1][j], d[i-1][j-weight[i]] + value[i])
#可知,d[i][j],只由上一行,即第i-1行决定
#且只由第i-1行的第j列及之前(左侧)的值决定
#则可以将i这个维度舍去,每次遍历时将第i-1行复用至第i行

dp = [0] * (N+1)
#dp[j] 代表容量为j的背包所能装的最大价值
#dp[j] = dp[j] 当 j < weihgt[i]
#dp[j] = max(dp[j], dp[j-weight[i] + value[i]]) 当 j >= weight[i]

#初始化
#由递推公式可知,只要dp初始化比计算的价值小,就可以成功覆盖
dp[0] = 0

#遍历顺序为行——列,因为我们是要复用每一行
for i in range(M):
    #倒序遍历的原因
    #由于每个j都需要用到左侧的值
    #如果先更新了j,那j之后的值就错乱了(左侧被覆盖)
    #且只需要遍历到weight[i],因为j比weight[i]小时,dp[j]不改动
    for j in range(N, weight[i]-1, -1):
        dp[j] = max(dp[j], dp[j-weight[i]]+ value[i])

print(dp[N])

lc416 分割等和子集问题

——0-1背包

class Solution:
    def canPartition(self, nums: List[int]) -> bool:
        sum_ = sum(nums)
        if sum_ % 2 == 1:
            return False
        #背包容量为target
        #物品价值 == 物品重量
        target = sum_ // 2

        #求容量为target的背包,能够装的最大价值
        #如果最大价值 < target,则背包无法装满
        #如果最大价值 == target,则背包可以装满
        #由于价值==重量,所以不会出现最大价值 > target的情况

        #dp[j]代表容量为j的背包装的最大价值
        dp = [0] * (target+1)
        dp[0] = 0
        for i in range(len(nums)):
            for j in range(target, nums[i]-1, -1):
                dp[j] = max(dp[j], dp[j- nums[i]] + nums[i])
        if dp[target] == target:
            return True
        return False

lc1049 最后一块石头的重量II

与lc416分割等和子集基本一样

尽可能分为两堆重量相近的石堆

则以sum // 2为背包容量,石头重量==石头价值,求背包最大价值

class Solution:
    def lastStoneWeightII(self, stones: List[int]) -> int:
        target = sum(stones) // 2
        dp = [0] * (target+1)
        for i in range(len(stones)):
            for j in range(target, stones[i]-1, -1):
                dp[j] = max(dp[j], dp[j-stones[i]] + stones[i])
        return sum(stones) - 2 * dp[target]

lc494 目标和

二维方法

class Solution:
    def findTargetSumWays(self, nums: List[int], target: int) -> int:
        if (sum(nums) + target) % 2 != 0:
            return 0
        if sum(nums) < abs(target):
            return 0
        positives = (sum(nums) + target) // 2
        n = len(nums)
        #dp[i][j] 代表用【0-i】的物品,填满容量为j的背包共有dp[i][j]种方法
        #当j >= nums[i]   dp[i][j] = dp[i-1][j] + dp[i-1][j-nums[i]]
        #当j < nums[i] dp[i][j] = d[i-1][j]

        #求dp[n-1][positives] 即用nums装满容量为positives的背包的方法数
        dp = [[0] * (positives+1) for _ in range(n)]

        #初始化
        if positives >= nums[0]:
            dp[0][nums[0]] = 1
        
        zero_nums = 0
        for i in range(n):
            if nums[i] == 0:
                zero_nums += 1
            dp[i][0] = pow(2, zero_nums)
        
        for i in range(1, n):
            for j in range(1, positives+1):
                if j < nums[i]:
                    dp[i][j] = dp[i-1][j]
                else:
                    dp[i][j] = dp[i-1][j] + dp[i-1][j-nums[i]]
        return dp[n-1][positives]

一维方法

class Solution:
    def findTargetSumWays(self, nums: List[int], target: int) -> int:
        if (sum(nums) + target) %2 != 0:
            return 0
        if sum(nums) < abs(target):
            return 0
        
        positives = (sum(nums) + target) // 2
        #去掉维度i之后,遍历到i的位置,代表可以使用【0-i】物品
        #只不过我们在不断更新、复用dp这一行
        #dp[j] 表示填满容量为j的背包有dp[j]种方法
        #dp[j] = dp[j] + dp[j-nums[i]]
        dp = [0] * (positives + 1)

        #初始化
        #这里还没开始遍历,所以认为物品0也是不可使用的
        #则填满背包容量为0的方法只能是1种(不放任何东西)
        dp[0] = 1
        
        for i in range(len(nums)):
            for j in range(positives, nums[i] - 1, -1):
                dp[j] = dp[j] + dp[j-nums[i]]
        return dp[positives]

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值