给定一个只包含正整数的非空数组。是否可以将这个数组分割成两个子集,使得两个子集的元素和相等。
注意:
每个数组中的元素不会超过 100
数组的大小不会超过 200
示例 1:
输入: [1, 5, 11, 5]
输出: true
解释: 数组可以分割成 [1, 5, 5] 和 [11].
示例 2:
输入: [1, 2, 3, 5]
输出: false
解释: 数组不能分割成两个元素和相等的子集.
public static boolean canPartition(int[] nums) {
//二维数组较一维数组空间利用率大,效率低,但是可以回溯,找到解的组成部分
int len=nums.length;
int sum=0;
for(int i : nums) {
sum+=i;
}
if(sum%2!=0) {
return false;
}
sum/=2;
int[][] dp=new int[len][sum+1];//dp[i]j]表示在前i个数字中,它们中取任意数相加在 不超过j情况下 所得到最大值
for(int i=0;i<=sum;i++) {//第一行要单独求出
if(nums[0]<=i) {
dp[0][i]=nums[0];
}
}
for(int i=1;i<len;i++) { //第i个物品只有选或不选dp[i][j]=max(dp[i-1][j],dp[i-1][j-nums[i]]+nums[i];
for(int j=1;j<=sum;j++) {//
if(j-nums[i]<0) {
dp[i][j]=dp[i-1][j];
}
else {
dp[i][j]=Math.max(dp[i-1][j],dp[i-1][j-nums[i]]+nums[i]);
}
}
}
if(dp[len-1][sum]==sum) {
return true;
}
return false;
}
public static boolean canPartition(int[] nums) {
//一维数组 效率高,空间消耗少,但不能回溯,
int len=nums.length;
int sum=0;
for(int i : nums) {
sum+=i;
}
if(sum%2!=0) {
return false;
}
sum/=2;
//优化思路
//dp[i][j]=max(dp[i-1][j],dp[i-1][j-nums[i]]+nums[i];
//dp[i][j]结果只与上一行的结果有关,因此我们可以使用一位数组保存上一行结果 利用一层for循环不断更新
int[] dp=new int[sum+1];
for(int i=0;i<=sum;i++) {//第一行单独求出
if(nums[0]<=i) {
dp[i]=nums[0];
}
}
for(int i=1;i<len;i++) {
for(int j=sum;j>=1;j--) { //注意 这里如果从前往后的话,会覆盖上一行dp,而后面求出的dp和前面dp相关,导致求出错误dp
if(j>=nums[i]) { //如果从后往前,那么求出的dp不会影响前面的dp
dp[j]=Math.max(dp[j],dp[j-nums[i]]+nums[i]);
}
}
}
if(dp[sum]==sum) {
return true;
}
return false;
}