LeetCode416. Partition Equal Subset Sum
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";
}