分割等和子集
题目描述
给定一个只包含正整数的非空数组。是否可以将这个数组分割成两个子集,使得两个子集的元素和相等。
注意:
每个数组中的元素不会超过 100
数组的大小不会超过 200
方法:动态规划之0-1背包问题
class Solution {
public boolean canPartition(int[] nums) {
if(nums == null || nums.length == 0) {
return false;
}
int sum = 0;
for(int num : nums) {
sum += num;
}
// 和为奇数时,不可能划分成两个和相等的集合
if (sum % 2 != 0) {
return false;
}
int n = nums.length;
sum = sum / 2;
boolean dp[][] = new boolean[n + 1][sum + 1];
// base case
for(int i = 0; i < n; i++) {
dp[i][0] = true;
}
for(int i = 1; i <= n; i++) {
for(int j = 1; j <= sum; j++) {
// 背包容量不足,不能装入第 i 个物品
if(j - nums[i - 1] < 0) {
dp[i][j] = dp[i - 1][j];
} else {
//比较 :num[i - 1]装入或不装入背包sum,判断此时能够恰好暂满背包
/*
1.如果不把 nums[i] 算入子集,或者说你不把这第 i 个物品装入背包,那么是否能够恰好装满背包,取决于上一个状态 dp[i-1][j],继承之前的结果。
2.如果把 nums[i] 算入子集,或者说你把这第 i 个物品装入了背包,那么是否能够恰好装满背包,取决于状态 dp[i - 1][j-nums[i-1]]。
*/
dp[i][j] = dp[i - 1][j] | dp[i - 1][j - nums[i - 1]];
}
}
}
return dp[n][sum];
}
}
时间复杂度 O(nsum),空间复杂度 O(nsum)。
动态规划进行空间复杂度优化:数组降维(状态压缩)
class Solution {
public boolean canPartition(int[] nums) {
if(nums == null || nums.length == 0) {
return false;
}
int sum = 0;
for(int num : nums) {
sum += num;
}
// 和为奇数时,不可能划分成两个和相等的集合
if (sum % 2 != 0) {
return false;
}
int n = nums.length;
sum = sum / 2;
boolean dp[] = new boolean[sum + 1];
// base case
dp[0] = true;
for(int i = 0; i < n; i++) {
//唯一需要注意的是 j 应该从后往前反向遍历,因为每个物品(或者说数字)只能用一次,以免之前的结果影响其他的结果。
for(int j = sum; j >= 0; j--) {
// 背包容量足,能装入第 i 个物品
if(j - nums[i] >= 0) {
dp[j] = dp[j] | dp[j - nums[i]];
}
}
}
return dp[sum];
}
}
时间复杂度 O(n*sum),空间复杂度 O(sum)。