问题:
给定一组数,请问是否将其分为两个数组,使得二者和相等。
Note: Each of the array element will not exceed 100. The array size will not exceed 200. Example 1: Input: [1, 5, 11, 5] Output: true Explanation: The array can be partitioned as [1, 5, 5] and [11]. Example 2: Input: [1, 2, 3, 5] Output: false Explanation: The array cannot be partitioned into equal sum subsets.
解法:DP(动态规划) 0-1 knapsack problem(0-1背包问题)
1.确定【状态】:
- 可选择的数:前 i 个数
- 和:s:0~全元素和/2
2.确定【选择】:
- 选择当前的数nums[i]
- 不选择当前的数nums[i]
3. dp[i][s]的含义:
前 i 个数中,组成和=s 的可能是否存在。
4. 状态转移:
dp[i][s]= OR {
- 选择 nums[i]:dp[i-1][s-nums[i]]:=前 i-1 个元素可组成和为 s-nums[i] 的可能性
- 不选择 nums[i]:dp[i-1][s]:=前 i-1 个元素可组成和为 s 的可能性
}
5. base case:
- dp[0][s]=false
- dp[i][0]=true
- dp[0][0]=true
代码参考:
1 class Solution { 2 public: 3 //dp[i][s]: in first i items, sum is s, exists? 4 //case_1,choose i-th item: dp[i-1][s-val[i]] 5 //case_2,don't choose: dp[i-1][s] 6 //dp[i][s] = case_1 OR case_2 7 //base case: dp[0][s] = false 8 //dp[i][0] = true 9 //dp[0][0] = true 10 bool canPartition(vector<int>& nums) { 11 bool res; 12 int sum = 0; 13 for(int n:nums){ 14 sum+=n; 15 } 16 if(sum%2) return false; 17 sum/=2; 18 vector<vector<bool>> dp(nums.size()+1, vector<bool>(sum+1, false)); 19 dp[0][0] = true; 20 for(int i = 1; i<=nums.size(); i++) { 21 for(int s = 1; s<=sum; s++) { 22 if(s-nums[i-1]<0) dp[i][s] = dp[i-1][s]; 23 else dp[i][s] = dp[i-1][s-nums[i-1]] || dp[i-1][s]; 24 } 25 } 26 return dp[nums.size()][sum]; 27 } 28 };
♻️ 优化:空间复杂度:2维->1维
去掉 i
将 s 倒序遍历。
if(s-nums[i-1]<0) dp[i][s] = dp[i-1][s];
else dp[i][s] = dp[i-1][s-nums[i-1]] || dp[i-1][s];
上述状态转移中,i 代表行,s 代表列
则都为 更新本行 : 使用上一行的 本列 和 前面的列 来计算覆盖本列。
顺序遍历的话,更新后面列的时候,会用到已经被覆盖掉的本列。而我们期望用的是上一行的本列。
因此 将 s 倒序遍历。更新前面的列,用的上一行本列,还未被本行操作更新。
代码参考:
1 class Solution { 2 public: 3 //dp[i][s]: in first i items, sum is s, exists? 4 //case_1,choose i-th item: dp[i-1][s-val[i]] 5 //case_2,don't choose: dp[i-1][s] 6 //dp[i][s] = case_1 OR case_2 7 //base case: dp[0][s] = false 8 //dp[i][0] = true 9 //dp[0][0] = true 10 bool canPartition(vector<int>& nums) { 11 bool res; 12 int sum = 0; 13 for(int n:nums){ 14 sum+=n; 15 } 16 if(sum%2) return false; 17 sum/=2; 18 vector<bool> dp(sum+1, false); 19 dp[0] = true; 20 for(int i = 1; i<=nums.size(); i++) { 21 for(int s = sum; s>0; s--) { 22 if(s-nums[i-1]>=0) { 23 dp[s] = dp[s-nums[i-1]] || dp[s]; 24 } 25 } 26 } 27 return dp[sum]; 28 } 29 };