416. Partition Equal Subset Sum
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.
题目:根据所给的一串元素,全为正数,判断是否能分为两个和相等的子集。
首先看到题目,首先第一直觉就是既然要求两个相等的子集,那么就可以先求出整个数组的和sum,然后在数组中选择若干元素是否能满足和为sum/2即可。
关键是怎么取元素,取多少个。简单的想法就是遍历所有的情况,用DFS。对每个元素分别考虑取和不取时的情况,当所有元素都判断过,则返回。但是在运行时间上超过了限制。Time Limits Exceeded。
#include <iostream>
#include<vector>
#include<algorithm>
#include<numeric>
using namespace std;
class Solution3 {
public:
bool canPartition(vector<int>& nums) {
if (nums.size() == 1)
return false;
int sum = accumulate(nums.begin(), nums.end(), 0);
if (sum % 2 != 0)
return false;
//只需找到所有元素和的一半 对应的组合即可
sum /= 2;
int result = DFS(nums, sum, 0);
return result;
}
private:
int DFS(vector<int>& nums, int remain, int start) {
if (start == nums.size())
return remain == 0 ? 1 : 0;
if (remain < 0)
return 0;
return DFS(nums, remain - nums[start], start + 1)
+ DFS(nums, remain, start + 1);
}
};
如果我们要判断n个元素中的子集是否能组成和为sum,那么对于nums中的每个元素,我们都需要考虑选择和不选择该元素时对sum的影响,假设现在在判断第i个元素,此时前i-1个元素所能组成的和的集合记为sum(i-1) = {sum1,sum2,...},如果取nums[i]时,那么sum(i) = {sum1+nums[i], sum2+nums[2],....},如果不取nums[i],那么sum(i)=sum(i-1) = {sum1,sum2,...},最终对于第i个元素,我们的和的集合更新为sum(i)={sum1,sum2,sum1+nums[i], sum2+nums[2],...}。
以上的分析造成集合sum(i)的大小是在不断扩大的,由于题目的特殊性,所有的元素均为正数,由此我们知道集合sum(i)中的元素肯定也都是正数,同时我们需要求的是在sum(i)这个集合中是否存在target,因此我们可以事先将sum集合的范围设置为[0,target],如果前面集合出现了超过target的和,那么这个和对于我们来说是没用的,因为后面的元素在这个和的基础上进行操作的话必然也是会大于target的。
因此可以设置一个二维的数组dp[nums.size()+1][target+1],dp[i][j]表示前i个元素是否是组合成和为j,如果能的话则值为true,否则为false。
dp[i][j] = dp[i-1][j]
dp[i][j] = dp[i-1][j-nums[i]].
#include <iostream>
#include<vector>
#include<algorithm>
#include<numeric>
using namespace std;
class Solution {
public:
bool canPartition(vector<int>& nums) {
if (nums.size() == 1)
return false;
int sum = accumulate(nums.begin(), nums.end(), 0);
if (sum % 2 != 0)
return false;
//只需找到所有元素和的一半 对应的组合即可
sum /= 2;
//dp[i][j]表示当前数组中的前i个元素是否能组成和为j
vector<vector<bool>> dp(nums.size() + 1, vector<bool>(sum + 1));
dp[0][0] = true;
for (int i = 1; i < nums.size() + 1; i++) {
dp[i][0] = true;
}
for (int j = 1; j < sum + 1; j++) {
dp[0][j] = false;
}
for (int i = 1; i < nums.size() + 1; i++)
{
for (int j = 1; j < sum + 1; j++)
{
dp[i][j] = dp[i - 1][j];
if (j - nums[i - 1] >= 0)
dp[i][j] = dp[i][j] || dp[i - 1][j - nums[i - 1]];
}
}
return dp[nums.size()][sum];
}
};
class Solution2 {
public:
bool canPartition(vector<int>& nums) {
if (nums.size() == 1)
return false;
int sum = accumulate(nums.begin(), nums.end(), 0);
if (sum % 2 != 0)
return false;
//只需找到所有元素和的一半 对应的组合即可
sum /= 2;
//dp[i]表示当前数组中是否有相应的组合的和的值为i
vector<int> dp(sum + 1, false);
dp[0] = true;
for (size_t i = 0; i < nums.size(); i++)
{
//注意这里是反向
for (int j = sum; j > 0; j--)
if (j >= nums[i])
dp[j] = dp[j] || dp[j - nums[i]];
}
return dp[sum];
}
};
int main()
{
Solution sln;
vector<int> testcase{ 1,2,5 };
cout << sln.canPartition(testcase) << endl;
std::cout << "Hello World!\n";
}