问题描述
问题分析
- 给定一个非空正整数数组,能否将数组分成两部分(子集),使两部分和相等。本质上就是在一个数组中取任意个数相加,能否加出 sum/2,是一个简化版的背包问题。
- 首先,如果
sum
为奇数,肯定找不出这样的两部分,直接返回false即可。
- 当为偶数时,才采用类似处理背包问题的动态规划法,只不过该题更为简单,没有weight,少一个参数。具体见代码实现中的注释
代码实现
public boolean canPartition(int[] nums) {
if (nums == null || nums.length == 0) {
return false;
}
int sum = 0;
for (int i = 0; i < nums.length; ++i) {
sum += nums[i];
}
return (sum & 1) == 0 && findHalf(nums, nums.length - 1, sum/2);
}
public boolean findHalf(int[] nums, int i, int remain) {
if (remain == 0) {
return true;
}
if (i < 0 || remain < 0) {
return false;
}
return findHalf(nums, i - 1, remain) || findHalf(nums, i - 1, remain - nums[i]);
}
public boolean canPartition(int[] nums) {
if (nums == null || nums.length == 0) {
return false;
}
int sum = 0;
for (int i = 0; i < nums.length; ++i) {
sum += nums[i];
}
if ((sum & 1) != 0) {
return false;
}
int col = sum / 2 + 1;
boolean[][] dp = new boolean[nums.length][col];
dp[0][0] = true;
for (int j = 1; j < col; ++j ) {
dp[0][j] = (nums[0] == j);
}
for (int i = 1; i < nums.length; ++i) {
dp[i][0] = true;
for (int j = 1; j < col; ++j) {
dp[i][j] = dp[i - 1][j] || (j - nums[i] >= 0 && dp[i - 1][j - nums[i]]);
}
}
return dp[nums.length - 1][col - 1];
}
public boolean canPartition(int[] nums) {
if (nums == null || nums.length == 0) {
return false;
}
int sum = 0;
for (int i = 0; i < nums.length; ++i) {
sum += nums[i];
}
if ((sum & 1) != 0) {
return false;
}
int len = sum / 2 + 1;
boolean[] dp = new boolean[len];
dp[0] = true;
for (int j = 1; j < len; ++j) {
dp[j] = (nums[0] == j);
}
for (int i = 1; i < nums.length; ++i) {
for (int j = len - 1; j >= nums[i]; --j) {
dp[j] = dp[j] || dp[j - nums[i]];
}
}
return dp[len - 1];
}