diff --git a/problems/0416.分割等和子集.md b/problems/0416.分割等和子集.md index 52b48264..ecee1d83 100644 --- a/problems/0416.分割等和子集.md +++ b/problems/0416.分割等和子集.md @@ -32,7 +32,7 @@ ## 思路 -这道题目初步看,是如下两题几乎是一样的,大家可以用回溯法,解决如下两题 +这道题目初步看,和如下两题几乎是一样的,大家可以用回溯法,解决如下两题 * 698.划分为k个相等的子集 * 473.火柴拼正方形 @@ -62,7 +62,7 @@ 回归主题:首先,本题要求集合里能否出现总和为 sum / 2 的子集。 -那么来一一对应一下本题,看看背包问题如果来解决。 +那么来一一对应一下本题,看看背包问题如何来解决。 **只有确定了如下四点,才能把01背包问题套到本题上来。** @@ -77,9 +77,9 @@ 1. 确定dp数组以及下标的含义 -01背包中,dp[j] 表示: 容量为j的背包,所背的物品价值可以最大为dp[j]。 +01背包中,dp[j] 表示: 容量为j的背包,所背的物品价值最大可以为dp[j]。 -本题中每一个元素的数值即是重量,也是价值。 +本题中每一个元素的数值既是重量,也是价值。 **套到本题,dp[j]表示 背包总容量(所能装的总重量)是j,放进物品后,背的最大重量为dp[j]**。 @@ -106,9 +106,9 @@ 从dp[j]的定义来看,首先dp[0]一定是0。 -如果如果题目给的价值都是正整数那么非0下标都初始化为0就可以了,如果题目给的价值有负数,那么非0下标就要初始化为负无穷。 +如果题目给的价值都是正整数那么非0下标都初始化为0就可以了,如果题目给的价值有负数,那么非0下标就要初始化为负无穷。 -**这样才能让dp数组在递归公式的过程中取的最大的价值,而不是被初始值覆盖了**。 +**这样才能让dp数组在递推的过程中取得最大的价值,而不是被初始值覆盖了**。 本题题目中 只包含正整数的非空数组,所以非0下标的元素初始化为0就可以了。 @@ -202,15 +202,15 @@ class Solution { if(nums == null || nums.length == 0) return false; int n = nums.length; int sum = 0; - for(int num : nums){ + for(int num : nums) { sum += num; } //总和为奇数,不能平分 if(sum % 2 != 0) return false; int target = sum / 2; int[] dp = new int[target + 1]; - for(int i = 0; i < n; i++){ - for(int j = target; j >= nums[i]; j--){ + for(int i = 0; i < n; i++) { + for(int j = target; j >= nums[i]; j--) { //物品 i 的重量是 nums[i],其价值也是 nums[i] dp[j] = Math.max(dp[j], dp[j-nums[i]] + nums[i]); } @@ -220,6 +220,7 @@ class Solution { } ``` +二维数组版本(易于理解): ```java public class Solution { public static void main(String[] args) { @@ -288,46 +289,6 @@ false true false false false true true false false false false true false true false false false true true false false false true true ``` - -二维数组版本(易于理解): -```Java -class Solution { - public boolean canPartition(int[] nums) { - int sum = 0; - for (int i = 0; i < nums.length; i++) { - sum += nums[i]; - } - - if (sum % 2 == 1) - return false; - int target = sum / 2; - - //dp[i][j]代表可装物品为0-i,背包容量为j的情况下,背包内容量的最大价值 - int[][] dp = new int[nums.length][target + 1]; - - //初始化,dp[0][j]的最大价值nums[0](if j > weight[i]) - //dp[i][0]均为0,不用初始化 - for (int j = nums[0]; j <= target; j++) { - dp[0][j] = nums[0]; - } - - //遍历物品,遍历背包 - //递推公式: - for (int i = 1; i < nums.length; i++) { - for (int j = 0; j <= target; j++) { - //背包容量可以容纳nums[i] - if (j >= nums[i]) { - dp[i][j] = Math.max(dp[i - 1][j], dp[i - 1][j - nums[i]] + nums[i]); - } else { - dp[i][j] = dp[i - 1][j]; - } - } - } - - return dp[nums.length - 1][target] == target; - } -} -``` ### Python: ```python class Solution: @@ -341,6 +302,7 @@ class Solution: dp[j] = max(dp[j], dp[j - nums[i]] + nums[i]) return target == dp[target] ``` + ### Go: ```go // 分割等和子集 动态规划 @@ -369,46 +331,6 @@ func canPartition(nums []int) bool { } ``` -```go -func canPartition(nums []int) bool { - /** - 动态五部曲: - 1.确定dp数组和下标含义 - 2.确定递推公式 - 3.dp数组初始化 - 4.dp遍历顺序 - 5.打印 - **/ - //确定和 - var sum int - for _,v:=range nums{ - sum+=v - } - if sum%2!=0{ //如果和为奇数,则不可能分成两个相等的数组 - return false - } - sum/=2 - //确定dp数组和下标含义 - var dp [][]bool //dp[i][j] 表示: 前i个石头是否总和不大于J - //初始化数组 - dp=make([][]bool,len(nums)+1) - for i,_:=range dp{ - dp[i]=make([]bool,sum+1) - dp[i][0]=true - } - for i:=1;i<=len(nums);i++{ - for j:=1;j<=sum;j++{//j是固定总量 - if j>=nums[i-1]{//如果容量够用则可放入背包 - dp[i][j]=dp[i-1][j]||dp[i-1][j-nums[i-1]] - }else{//如果容量不够用则不拿,维持前一个状态 - dp[i][j]=dp[i-1][j] - } - } - } - return dp[len(nums)][sum] -} -``` - ### javaScript: ```js