今日任务
目录
01背包问题 二维数组
01背包问题 一维/滚动数组
416.分割等和子集 - Medium
理论基础:代码随想录
01 背包
有n件物品和一个最多能背重量为w 的背包。第i件物品的重量是weight[i],得到的价值是value[i] 。每件物品只能用一次,求解将哪些物品装入背包里物品价值总和最大。
暴力解法:
- 每一件物品其实只有两个状态,取或者不取,所以可以使用回溯法搜索出所有的情况
- 暴力的解法是指数级别的时间复杂度。进而才需要动态规划的解法来进行优化
二维dp数组
- 1 确定dp数组及含义:
dp[i][j] 表示从下标为[0-i]的物品里任意取,放进容量为j的背包,价值总和最大是多少
- 2 递推公式:dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i])
- 3 dp数组初始化:所有dp[i][0]均为0;当 j < weight[0]的时候 dp[0][j] 是 0,当j >= weight[0]时,dp[0][j] 是value[0]
- 4 确定遍历顺序:有两个遍历的维度,物品与背包重量,先遍历物品更好理解
- 5 推导dp数组:如下,无参数版
def test_2_wei_bag_problem1(): weight = [1, 3, 4] value = [15, 20, 30] bagweight = 4 # 二维数组 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]) print(dp[len(weight) - 1][bagweight]) test_2_wei_bag_problem1()
理论基础:代码随想录
推荐 使用一维dp数组的写法,比较直观简洁,而且空间复杂度还降了一个数量级
在二维dp数组的基础上,其实可以把dp[i - 1]那一层拷贝到dp[i]上,表达式完全可以是:dp[i][j] = max(dp[i][j], dp[i][j - weight[i]] + value[i]);与其把dp[i - 1]这一层拷贝到dp[i]上,不如只用一个一维数组了,只用dp[j](一维数组,也可以理解是一个滚动数组)。
这就是滚动数组的由来,需要满足的条件是上一层可以重复利用,直接拷贝到当前层。
滚动dp数组:
- 1 dp[j]:容量为j的背包,所背的物品价值可以最大为dp[j]
- 2 递推公式:dp[j]有两个选择,一个是取自己dp[j] 相当于 二维dp数组中的dp[i-1][j],即不放物品i;一个是取dp[j - weight[i]] + value[i],即放物品i,指定是取最大的
dp[j] = max(dp[j], dp[j - weight[i]] + value[i])
- 3 初始化:都初始为0就可以了
- 4 遍历顺序:一维dp遍历的时候,背包是从大到小,倒序遍历是为了保证物品i只被放入一次,每次取得状态不会和之前取得状态重合,这样每种物品就只取一次了;两个嵌套for循环的顺序,先遍历物品嵌套遍历背包容量,不可颠倒
- 5 推导dp数组:无参数版
def test_1_wei_bag_problem(): weight = [1, 3, 4] value = [15, 20, 30] bagWeight = 4 # 初始化 dp = [0] * (bagWeight + 1) for i in range(len(weight)): # 遍历物品 for j in range(bagWeight, weight[i] - 1, -1): # 遍历背包容量 dp[j] = max(dp[j], dp[j - weight[i]] + value[i]) print(dp[bagWeight]) test_1_wei_bag_problem()
题目链接:力扣-416. 分割等和子集
给你一个 只包含正整数 的 非空 数组
nums
。请你判断是否可以将这个数组分割成两个子集,使得两个子集的元素和相等。
提示: 转换成背包容易为 1/2 数组和的 01背包问题
class Solution:
def canPartition(self, nums: List[int]) -> bool:
if sum(nums) % 2 == 1: return False
bagWeight = sum(nums) // 2
dp = [0] * (bagWeight + 1)
for i in range(len(nums)): # 遍历物品
for j in range(bagWeight, nums[i] - 1, -1): # 遍历背包容量
dp[j] = max(dp[j], dp[j - nums[i]] + nums[i])
if dp[bagWeight] == bagWeight:
return True
else:
return False