-
背包问题
-
二维
- 代码随想录 (programmercarl.com)
-
一维
- 01背包-滚动数组
-
416分割等和子集
- 代码随想录 (programmercarl.com)
-
第一印象
- 可以分割成等和子集的前提是自己元素和为偶数。所以首先需要判定子元素和。暴力方法可以通过回溯寻找子子集,但超时。尝试使用线性规划,但一时想不出dp数组的意义。
-
讲解观后感
- 只有确定了如下四点,才能把01背包问题套到本题上来。
- 背包的体积为sum / 2
- 背包要放入的商品(集合里的元素)重量为 元素的数值,价值也为元素的数值
- 背包如果正好装满,说明找到了总和为 sum / 2 的子集。
- 背包中每一个元素是不可重复放入。
- 01背包中,dp[j] 表示: 容量为j的背包,所背的物品价值最大可以为dp[j]。
- 本题中每一个元素的数值既是重量,也是价值。
- 套到本题,dp[j]表示 背包总容量(所能装的总重量)是j,放进物品后,背的最大重量为dp[j]。
- 想清楚了背包容量就是价值的道理,本题就容易推进了。
- 当容量为target时,由于我们背包问题的方法是求解最大价值,所以如果可以填满,那么一定会填满。
-
解题代码
-
// 时间复杂度O(n^2) 空间复杂度O(n) func canPartition(nums []int) bool { sum := 0 for _, num := range nums { sum += num } // 如果 nums 的总和为奇数则不可能平分成两个子集 if sum % 2 == 1 { return false } target := sum / 2 dp := make([]int, target + 1) for _, num := range nums { for j := target; j >= num; j-- { if dp[j] < dp[j - num] + num { // dp[j] = max(dp[j], dp[j-num] + num) dp[j] = dp[j - num] + num } } } /* for i:=0;i<len(nums);i++ { for j:=target;j>=nums[i];j-- { if dp[j] < dp[j-nums[i]] + nums[i] { dp[j] = dp[j-nums[i]] + nums[i] } } } */ return dp[target] == target } - 二维
-
// 时间复杂度O(n^2) 空间复杂度O(n) func canPartition(nums []int) bool { sum := 0 for _, num := range nums { sum += num } // 如果 nums 的总和为奇数则不可能平分成两个子集 if sum % 2 == 1 { return false } ans := false target := sum / 2 dp := make([][]int, len(nums)) for l:=0;l<len(nums);l++ { dp[l] = make([]int, target+1) } for i:=0;i<len(nums);i++ { dp[i][0] = 0 } for i:=0;i<target+1;i++ { dp[0][i] = 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] } else { dp[i][j] = mx(dp[i-1][j], dp[i-1][j-nums[i]] + nums[i]) } if j == target { if dp[i][j] == target { ans = true } } } } return ans } func mx(x int, y int) int { if x > y { return x } else { return y } }