416. 分割等和子集
题目链接:link
超时的思路
简单分析一下题目,要把一个数组分割为两个元素之和相等的数组,就是找出元素之和为原数组元素之和的二分之一的分法,就相当于背包问题,从数组中拿出元素放到背包里,使得背包里元素之和为一个固定的数(不用考虑剩下的数)。
错误的代码:超时。
class Solution {
int s=0;
int t=0;
int sum=0;
public boolean canPartition(int[] nums) {
for(int i=0;i<nums.length;i++)
t+=nums[i];
if((int)(t/2*2)!=t)return false;
t/=2;
toSum(nums,0);
if(s==1)return true;
return false;
}
public void toSum(int nums[],int index){
if(sum==t)s=1;
if(index<nums.length){
sum+=nums[index];
toSum(nums,index);
index--;
sum-=nums[index];
index++;
toSum(nums,index);
}
}
}
0-1背包
下面是用二维数组做dp数组实现了,就是简单的套用公式,在本题中可以看成重量和价值相等。
class Solution {
public boolean canPartition(int[] nums) {
int n=nums.length;
int sum=0;
for(int i=0;i<n;i++)sum+=nums[i];
//和必须是偶数,如果不是就不存在这样的两个数组
if(sum/2*2!=sum)return false;
int t=sum/2;
int dp[][]=new int[n][t+1];
for(int i=1;i<n;i++)
{
for(int j=1;j<=t;j++)
{
if(j<nums[i])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[n-1][t]==t)return true;
return false;
}
}
dp数组表示的是容量为i的背包里最大放了多大的数,当dp[target]==target意味着可以放满背包。
注意这里使用了一维的滚动数组来构造dp,所以循环的顺序必须从后往前更新数组,必须先遍历物品再遍历背包,其实也是套公式。
class Solution {
public boolean canPartition(int[] nums) {
if(nums == null || nums.length == 0) return false;
int n = nums.length;
int sum = 0;
for(int num : nums) {
sum += num;
}
//和必须是偶数,如果不是就不存在这样的两个数组
if(sum % 2 != 0) return false;
int target = sum / 2;
int[] dp = new int[target + 1];
for(int i = 0; i < n; i++) {
for(int j = target; j >= nums[i]; j--) {
dp[j] = Math.max(dp[j], dp[j-nums[i]] + nums[i]);
}
}
return dp[target] == target;
}
}