给定一个只包含正整数的非空数组。是否可以将这个数组分割成两个子集,使得两个子集的元素和相等。
注意:
每个数组中的元素不会超过 100
数组的大小不会超过 200
示例 1:
输入: [1, 5, 11, 5]
输出: true
解释: 数组可以分割成 [1, 5, 5] 和 [11].
来源:力扣(LeetCode)
用动态规划的方法,二维布尔dp数组存储的是nums数组下标从0到i的数字是否有若干个数字和为j,状态转移方程如代码所示:
class Solution {
public boolean canPartition(int[] nums) {
int n=nums.length;
if(n<2)
return false;
int sum=0,maxNum=0;
for(int num:nums){
sum+=num;
maxNum=Math.max(maxNum,num);
}
if(sum%2!=0)
return false;
int target=sum/2;
if(maxNum>target)
return false;
//dp数组的含义
//dp[i][j]代表nums数组下标从0到i的数字是否有若干个数字和为j
boolean [][]dp=new boolean[n][target+1];
for(int i=0;i<n;i++){
dp[i][0]=true;
}
dp[0][nums[0]]=true;
for(int i=1;i<n;i++){
int num=nums[i];
for(int j=1;j<=target;j++){
if(num>j) //这个数字比该位置需要的总和还大,直接舍去
dp[i][j]=dp[i-1][j];
else
dp[i][j]=dp[i-1][j] || dp[i-1][j-num];
}
}
return dp[n-1][target];
}
}
这里的代码还可以进一步优化:把二维数组转化为一维数组:
且需要注意的是第二层的循环我们需要从大到小计算,因为如果我们从小到大更新dp值,那么在计算 dp[j] 值的时候,dp[j-nums[i]]已经是被更新过的状态,不再是上一行的dp值。
class Solution {
public boolean canPartition(int[] nums) {
int n=nums.length;
if(n<2)
return false;
int sum=0,maxNum=0;
for(int num:nums){
sum+=num;
maxNum=Math.max(maxNum,num);
}
if(sum%2!=0)
return false;
int target=sum/2;
if(maxNum>target)
return false;
//dp数组的含义
//dp[i]代表遍历到此,是否有若干个数字和为j
boolean []dp=new boolean[target+1];
dp[0]=true;
for(int i=0;i<n;i++){
int num=nums[i];
for(int j=target;j>=num;j--){
dp[j]=dp[j] | dp[j-num];
}
}
return dp[target];
}
}