Given a non-empty array containing only positive integers, find if the array can be partitioned into two subsets such that the sum of elements in both subsets is equal.
Note:
- Each of the array element will not exceed 100.
- The array size will not exceed 200.
Example 1:
Input: [1, 5, 11, 5] Output: true Explanation: The array can be partitioned as [1, 5, 5] and [11].
Example 2:
Input: [1, 2, 3, 5] Output: false Explanation: The array cannot be partitioned into equal sum subsets.
Seen this question in a real interview before?
把一个数组分成两组,看和是否相等。注意这不是连续数组。比如[3 4 6 5] 不能用区间和计算
思路:1个数选或不选。
首先求得总和,要求选其中的数的和是总和的一半。
建立矩阵dp[i][j],表示j这个和能否由前i个数组成。
边界条件:
(1) dp[0][0]=true
(2)dp[i][0]=true,对于所有的i只要一个数都不选,就可以了
(3)dp[0][j]=false 0个数不能组成1一上的数,只能组成0
传递条件:
dp[i][j]=dp[i-1][j]||dp[i-1][j-nums[i]] 说明,如果前i-1个数能组成j,那么前i个数一定能组成j,只要不加nums[i];如果前i-1个数能组成j-nums[i],那么前i个数能组成j,只要将nums[i]加上即可。这两种情况由一种满足,当前值就是true。
class Solution {
public:
bool canPartition(vector<int>& nums) {
int n=nums.size();
if(n<=1) return false;
int sum=accumulate(nums.begin(),nums.end(),0);
if(sum&1) return false;
sum/=2;
vector<vector<bool>> dp(n+1,vector<bool>(sum+1,false));//这里的i,从1位置表示几个数,sum也是从1开始计算,因为0个数也要考虑,sum是求和的值,也包括0这个意义
dp[0][0]=true;
for(int i=1;i<=n;i++)
dp[i][0]=true;
//初始化dp[0][j]已经是false
for(int i=1;i<=n;i++)
for(int j=1;j<=sum;j++)
{
dp[i][j]=dp[i-1][j];//
if(j>=nums[i-1])//有等号,和可以是0
{
dp[i][j]=dp[i-1][j]||dp[i-1][j-nums[i-1]];//注意nums[i-1]是从0开始记得
}
}
return dp[n][sum];
}
};
空间优化:
发现横坐标至于前面一行有关,而且j的遍历也是从小到大的。所以j应该从大到小更新,否则会覆盖旧知
class Solution {
public:
bool canPartition(vector<int>& nums) {
int n=nums.size();
if(n<=1) return false;
int sum=accumulate(nums.begin(),nums.end(),0);
if(sum&1) return false;
sum/=2;
vector<bool> dp(sum+1,false);//这里的i,从1位置表示几个数,sum也是从1开始计算,因为0个数也要考虑,sum是求和的值,也包括0这个意义
dp[0]=true;
//初始化dp[0][j]已经是false
for(int i=1;i<=n;i++)
for(int j=sum;j>=1;j--)
{
if(j>=nums[i-1])//有等号,和可以是0
{
dp[j]=dp[j]||dp[j-nums[i-1]];//注意nums[i-1]是从0开始记得
}
}
return dp[sum];
}
};