给定一个只包含正整数的非空数组。是否可以将这个数组分割成两个子集,使得两个子集的元素和相等。
注意:
每个数组中的元素不会超过 100
数组的大小不会超过 200
示例 1:
输入: [1, 5, 11, 5]
输出: true
解释: 数组可以分割成 [1, 5, 5] 和 [11].
示例 2:
输入: [1, 2, 3, 5]
输出: false
解释: 数组不能分割成两个元素和相等的子集.
本题我的第一想法就是回溯,利用晦回溯遍历整个数组的所有子数组,观察子数组的和是否是数组和的1/2。 在利用回溯进行选择的时候,对"选择"(数组)往往先通过排序,利用有序性,可以帮助剪枝,也可以帮助去重。两种回溯的写法如下:
bool comp(int & a, int & b){
return a > b;
}
class Solution {
public:
bool DFS(vector<int> & nums, int ptr, int curr_sum, int target)
{
int remain = target - curr_sum;
for (int i = ptr; i < nums.size(); i++)
{
if (nums[i]==remain)
return true;
else if (nums[i] < remain)
{
if (DFS(nums, i+1, curr_sum+nums[i], target))
return true;
}
//nums[i] > remain的情况不用管,直接下一步
}
return false;//全部都搜过一遍还是没有return true
}
bool canPartition(vector<int>& nums)
{
int n = nums.size();
int tot_sum = 0;
for (int i = 0; i < n; i++)
{
tot_sum += nums[i];
}
if (tot_sum % 2 != 0)
return false;
int div_sum = tot_sum / 2;
sort(nums.begin(), nums.end(), comp);//降序排列
if (nums[0] > div_sum) return false;//第一个就已经过半了
else {
if (DFS(nums, 0, 0, div_sum))
return true;
else return false;
}
}
};
class Solution {
public:
bool DFS(int target,vector<int>& nums,int j)
{
if(target==0)
return true;
if(j==nums.size())
return false;
if(target<0)
return false;
return DFS(target-nums[j],nums,j+1)||DFS(target,nums,j+1);
}
bool canPartition(vector<int>& nums) {
int sum=accumulate(nums.begin(),nums.end(),0);
sort(nums.rbegin(),nums.rend());
int target=sum/2;
if(sum%2==1)
return false;
if(nums[0]>target)
return false;
if(nums[0]==target)
return true;
return DFS(target,nums,0);
}
};
这道题目可以使用动态规划的思想,创建二维数组 (dp),包含 (n) 行 (target+1) 列,其中 (dp[i][j]) 表示从数组的 ([0,i])(左闭右闭) 下标范围内选取若干个正整数(可以是 (0) 个),是否存在一种选取方案使得被选取的正整数的和等于 (j)。初始时,(dp) 中的全部元素都是 (false)。
class Solution {
public:
bool canPartition(vector<int>& nums) {
int n = nums.size();
if (n < 2) {
return false;
}
int sum = accumulate(nums.begin(), nums.end(), 0);
int maxNum = *max_element(nums.begin(), nums.end());
if (sum & 1) {
return false;
}
int target = sum / 2;
if (maxNum > target) {
return false;
}
vector<vector<int>> dp(n, vector<int>(target + 1, 0));
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 (j >= num) {
dp[i][j] = dp[i - 1][j] | dp[i - 1][j - num];
} else {
dp[i][j] = dp[i - 1][j];
}
}
}
return dp[n - 1][target];
}
};