题目
给定一个只包含正整数的非空数组。是否可以将这个数组分割成两个子集,使得两个子集的元素和相等。
注意:
每个数组中的元素不会超过 100
数组的大小不会超过 200
示例 1:
输入: [1, 5, 11, 5]
输出: true
解释: 数组可以分割成 [1, 5, 5] 和 [11].
示例 2:
输入: [1, 2, 3, 5]
输出: false
解释: 数组不能分割成两个元素和相等的子集.
答案
class Solution {
public boolean canPartition(int[] nums) {
int len = nums.length;
int sum=0,maxNum=0;
if (len<2){//当数组元素小于2时不可分,返回false
return false;
}
for (int num:nums){
sum += num;
maxNum = Math.max(maxNum, num);
}
if (sum%2!=0){//所有元素之后为奇数,不可分,返回false
return false;
}
int target=sum/2;
if (maxNum>target){//最大元素大于目标数,不可能分成两个和相等的数组
return false;
}
//创造一个[len][target+1]的二维数组,其中len表示数组元素个数,target表示目标值。
//数组从0开始,所以i-1为当前要分的个数,可通过nums[i]找到数组元素值,j表示当前要达到的目标值,在这i-1个元素的情况下,可以加出j则为true
//需要得到len个元素是否能获得target所以为[len][target+1],最后[len-1][target]即为答案。
//该二维数组是可以加出j即为true,而不是要等分为两份为j的数组。
boolean[][] dp = new boolean[len][target+1];//boolean型数据默认为false。
//当j为0时,为true,可以理解为可以把元素分为空集和其他所有集,空集那部分则可以加为0。
for (int i=0;i<len;i++){
dp[i][0] = true;
}
//当i=0,可用元素只有一个,则只有j=此元素才能加出j。
dp[0][nums[0]] = true;
//从i=1开始,依次增加元素
for (int i=1;i<len;i++){
for (int j=1;j<target+1;j++){
//当最新的元素大于j时,加成j不需要这个元素,则是否能加成j与前几个元素情况相同。
//如:假设i=2,有三个元素1,2,3,j=6,此时为true;
// 当i+1为3后,第四个元素为7。7>6,那么是否能组成6与7无关,而与1,2,3有关,即dp[i][j]=dp[i-1][j]
if (j<nums[i]) {
dp[i][j] = dp[i - 1][j];
}
//当最新元素小于j时,如果dp[i-1][j]为true,即之前的元素就能加出j,那么此时dp[i][j]为true。
//如果dp[i-1][j]为false也有可能这个元素可以组成j,此情况可以转为某个数x+nums[i]=j,则x=j-nums[i]
//那么可以转化为前几个元素能否组成x,dp[i][j]=dp[i-1][x]=dp[i-1][j-nums[i]]
//上述两种情况有一个为true,则为true。
else {
dp[i][j] = (dp[i-1][j-nums[i]] | dp[i-1][j]);
}
}
}
return dp[len-1][target];
}
}