代码随想录day35 || 416 分割等和子集
背包问题
有n件物品和一个最多能背重量为 w 的背包。第i件物品的重量是 weight[i],得到的价值是 value[i] 。每件物品只能用一次,求解将哪些物品装入背包里物品价值总和最大。
copy
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
// pake
//
// @Description:
// @param weights: 物品i对应重量
// @param value: 物品i对应价值
// @param n: 背包容量
// @return int
func pake(weights, value []int, n int) {
// dp 五部曲
// 确定dp数组以及下标的含义: dp[i][j] 代表容量为j的背包装前i个物品获得的最大价值
// 递推公式 dp[i][j] = max(dp[i-1][j], dp[i-1][j-w(i)]+v(i))
// 初始化,背包容量为0初始化为0,物品0 初始化小于w(0)的背包价值为0. 大于初始化为v(0)
// 遍历,先物品后背包
// 打印
var dp = make([][]int, len(weights))
for i := 0; i < len(dp); i++ {
dp[i] = make([]int, n+1)
}
// dp[i][0] 初始化为0,dp[0][j] 看情况
for i := 0; i < len(dp); i++ {
for j := 0; j < len(dp[0]); j++ {
if j == 0 {
dp[i][j] = 0
} else if i == 0 {
if j >= weights[i] {
dp[i][j] = value[i]
} else {
dp[i][j] = 0
}
} else {
var v1, v2 int
v1 = dp[i-1][j]
if j-weights[i] >= 0 {
v2 = dp[i-1][j-weights[i]] + value[i]
}
fmt.Println(v1, v2)
dp[i][j] = max(v1, v2)
}
}
}
fmt.Println(dp)
}
copy
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
// pake
//
// @Description:
// @param weights: 物品i对应重量
// @param value: 物品i对应价值
// @param n: 背包容量
// @return int
func pake(weights, value []int, n int) {
// dp 五部曲
// 确定dp数组以及下标的含义: dp[j] 代表容量为j的背包装物品能够获得的最大价值
// 递推公式 dp[j] = max(dp[j], dp[j-w(i)]+v(i))
// 初始化,背包容量为0初始化为0,其他也初始化为0
// 遍历,先物品后背包,但是背包要反序遍历,因为递推公式依赖的是上一层循环的前一个元素,如果在遍历过程中修改了上一层的之前元素,那么之后的递推就是错误的, 可能导致一个物品多次使用的情况,
// 打印
var dp = make([]int, n+1)
for i := 0; i < len(weights); i++ {
for j := n; j > 0; j-- {
var v int
if j-weights[i] >= 0 {
v = dp[j-weights[i]] + value[i]
}
dp[j] = max(dp[j], v)
}
}
fmt.Println(dp)
}
416 分割等和子集
copy
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
func canPartition(nums []int) bool {
// 本题主要难点在于,能否想到,通过将target = sum/2 ,并且填满target容量的背包从而转换成01背包问题
// 证明,01背包元素不重复,如果dp[target] == target, 使用了m个元素,和为target=sum/2,那么剩下元素和也为sum/2,从而出现两个子集和相同
// dp数组以及下标,dp[j] 表示容量j能装的最大价值,本题数组可以看作重量=价值
// 递推公式 dp[j] = max(dp[j], dp[j-w[j]]+v[j]) = max(dp[j], dp[j-nums[j]]+nums[j])
// 初始化 全部0
// 遍历顺序,外层物品正序,内层背包倒叙
// print
var sum, target int
for _, v := range nums {
sum += v
}
if sum % 2 == 1 { // 和为奇数无法等量划分
return false
}
target = sum / 2
dp := make([]int, target + 1)
for i:=0; i<len(nums); i++{
for j:=target; j>=nums[i]; j-- { //j>nums[i] 表示空间至少要比i所占用空间大,不然就无法放入,当然也可以在内部判断剪枝
dp[j] = max(dp[j], dp[j-nums[i]] + nums[i])
}
}
fmt.Println(dp)
if dp[target] == target{
return true
}
return false
}
func max (i, j int )int {
if i>j{
return i
}
return j
}
本文作者:周公瑾55
本文链接:https://www.cnblogs.com/zhougongjin55/p/18369118
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步