背包问题
01背包
Given
- n种物品,每种物品只有1个
- 每个物品有自己的重量、价值。
- 背包最大能装载的重量
动规五部曲——用二维数组
- 定义dp数组的含义:dp[ i ][ j ]表示[0,i]物品,任取放在容量为j的背包。
- 递推数组dp[i][j]:
(1)不放物品i,则所选的物品少1:dp[i-1][j]
(2)放物品i,则背包容量需要减去i的重量:dp[i-1][j-weight[i]]+value[i]
(3)dp[i][j]=max(dp[i-1][j],dp[i-1][j-weight[i]]+value[i]) - 如何初始化dp数组:dp[i][j]是由上面的dp[i-1][j]和左上方的某一个元素推至。所以需要初始化第一行和第一列,然后按照递推公式确定后续的所有。
(1)第一列初始化:dp[k][0]=0, k=0,…,n-1
(2)第一行初始化:dp[0][k]按照物品0的重量决定
(3)其余任意
动规五部曲——用一维数组
链接: 01背包问题用一维数组
把二维dp数组降为一维——滚动数组
- 定义dp[j]表示容量为j的背包能放最大价值为dp[j]的物品
- 递推数组:
滚动数组的每一次更新,比如第i次更新,就是表示对于物品i的决策。相当于在二维数组中,把下一行的值直接在本行进行更新,所以先前的dp[j]是存储了不放物品i的
dp[j]=max(dp[j],dp[j-weight[i]]+value[i]) - 初始化:
dp[0]=0
因为dp[j]是在自己原来的值和另外一个值取max,所以自己的初始化不能太大,不然会覆盖真实的情况。
所以其余全部可以初始化为0 - 遍历顺序
先遍历物品->再倒叙遍历背包(倒叙保证物品只被添加一次)
416. 分割等和子集
题目
给你一个 只包含正整数 的 非空 数组 nums 。请你判断是否可以将这个数组分割成两个子集,使得两个子集的元素和相等。
怎么靠到动态规划01背包问题
- 一开始我的笨蛋想法是从大到小遍历整个数组中的元素,用两个数组来装。对于元素i,如果当前数组sum1>sum2,就放到2,否则放到1中,但是这是错误的。对于【5,4,3,3,3】。
- 靠向背包问题:切割为和相等的两个数组。nums总和为sum时,即有一个背包容量为sum/2的背包,nums数组是物品的重量,找到把背包装满的物品。
solution(二维)
class Solution:
def canPartition(self, nums: List[int]) -> bool:
if sum(nums)%2==1:return False
package=int(sum(nums)/2) # 背包容量11
temp=[0]*(package+1)
dp=[]
for i in range(len(nums)):
dp.append(temp.copy())
# dp[i][j]=max(dp[i-1][j]],dp[i-1][j-nums[i]]+nums[i])
# if dp[i][package]==package:return True
# if traversal items finished, return False
for i in range(len(nums)):dp[i][0]=0
for j in range(len(dp[0])):
if j>=nums[0]:dp[0][j]+=nums[0]
for i in range(len(nums)):
for j in range(len(dp[0])):
# 我们的目的是尽可能地装满我们的背包容量,即最多的价值的物品
# 如果dp[-1][package]==package,说明我们决策完所有的物品后,能装到的最大价值(重量)如果与背包容量相等,则说明可以分割数组。否则不满足。
if j>=nums[i]:
dp[i][j]=max(dp[i-1][j],dp[i-1][j-nums[i]]+nums[i])
else:
dp[i][j]=dp[i-1][j]
if dp[-1][-1]==package:return True
return False
递推公式处需要进行判断,当前的容量是否可以装,可以装才有递推公式(选到底装还是不装),不可以装就只有一种情况。