代码随想录二刷Day42
今日任务
01 背包问题,你该了解这些!
01 背包问题,你该了解这些!滚动数组
416.分割等和子集
语言:Go
01背包理论基础
- 01背包:有n件物品和一个最多能背重量为w的背包,第i件物品的重量是weight[i],得到的价值是value[i],每件物品只能用一次,求解将哪些物品装入背包里物品价值总和最大
- 动规五部曲
① 确定dp数组及下标含义:dp[i][j]
表示从下标为[0-i]的物品任取,放入容量为j的背包中,价值总和最大是dp[i][j]
② 确定dp数组递归公式:分为两种情况,放第i个物品和不放第i个物品;
dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i]
③ 初始化dp数组:
dp[i][0]=0
if i > value[0] dp[0][j]=value[0]
④ 确定dp数组遍历顺序:先遍历物品还是背包是个难点
⑤ 举例推导dp数组:验证 - 滚动数组
即只使用一维数组代替二维数组
① 确定dp数组及下标含义:dp[j]
表示容量为j的背包中物品价值最大可以为dp[j]
② 确定dp数组递推公式:dp[j] = max(dp[j], dp[j - weight[i]] + value[i]
③ 初始化dp数组:全部初始化为0即可
④ 确定dp数组遍历顺序:
必须倒序遍历:保证每个物品只被放入一次
必须外层遍历物品内层遍历背包:否则相当于背包内只放入了一个物品
为什么二维数组没有这么多限制:数组每层的更新都要利用之前行/列的值,二维数组没有数据覆盖的问题,而一维数组的数据会被覆盖,所以要加以限制
⑤ 举例推导dp数组:验证
416. 分割等和子集
链接:https://leetcode.cn/problems/partition-equal-subset-sum/
func canPartition(nums []int) bool {
sum := 0
for i := 0; i < len(nums); i++ {
sum += nums[i]
}
if sum % 2 == 1 {
return false
}
target := sum / 2
sort.Slice(nums, func(i, j int) bool {
return nums[i] < nums[j]
})
//1.确定dp数组及下标含义:dp[i][j]表示从下标0-i的数据中取数据放入和为j的背包中的取法有dp[i][j]
//2.确定dp数组递归公式:分三种情况
//3.初始化dp数组:dp[i][0]=0,dp[0][nums[0]]=1
//4.确定dp数组遍历顺序:外层物品,内层背包
//5.举例推导dp数组
dp := make([][]int, len(nums))
for i := 0; i < len(nums); i++ {
dp[i] = make([]int, target + 1)
}
if target < nums[0] {
return false
}
dp[0][nums[0]] = 1
for i := 0; i < len(nums); i++ {
dp[i][0] = 0
}
for i := 1; i < len(nums); i++ {
for j := 1; j <= target; j++ {
if nums[i] == j {
dp[i][j] = dp[i - 1][j] + 1
} else if nums[i] < j {
dp[i][j] = dp[i - 1][j - nums[i]] + dp[i - 1][j]
} else if nums[i] > j {
dp[i][j] = dp[i - 1][j]
}
}
if dp[i][target] > 0 {
return true
}
}
return false
}
/*
0 1 2 3 4
1 0 1 0 0 0
2 0 1 1 1 0
5 0 1 1 1 0
0 1 2 3 4 5 6 7 8 9 10 11
1 0 1 0 0 0 0 0 0 0 0 0 0
5 0 1 0 0 0 1 1 0 0 0 0 0
5 0 1 0 0 0 2 2 0 0 0 1 1
11 0
*/
滚动数组改进
func canPartition(nums []int) bool {
sum := 0
for i := 0; i < len(nums); i++ {
sum += nums[i]
}
if sum % 2 == 1 {
return false
}
target := sum / 2
sort.Slice(nums, func(i, j int) bool {
return nums[i] < nums[j]
})
//1.确定dp数组及下标含义:dp[j]表示取出和为j的数据有dp[j]种可能
//2.确定dp数组递归公式:
//3.初始化dp数组:dp[i][0]=0,dp[0][nums[0]]=1
//4.确定dp数组遍历顺序:外层物品,内层背包
//5.举例推导dp数组
if nums[0] > target {
return false
} else if nums[0] == target {
return true
}
dp := make([]int, target + 1)
dp[nums[0]] = 1
for i := 1; i < len(nums); i++ {
for j := target; j >= nums[i]; j-- {
if j == nums[i] {
dp[j] = dp[j] + 1
} else {
dp[j] = dp[j] + dp[j - nums[i]]
}
}
if dp[target] > 0 {
return true
}
}
return false
}
/*
0 1 2 3 4
1 0 1 0 0 0
2 0 1 1 1 0
5 0 1 1 1 0
0 1 2 3 4 5 6 7 8 9 10 11
1 0 1 0 0 0 0 0 0 0 0 0 0
5 0 1 0 0 0 1 1 0 0 0 0 0
5 0 1 0 0 0 2 2 0 0 0 1 1
11 0
*/