题目详情:
1.回溯
根据这个题目,我们可以想到用回溯来做。加上这个数字或者减去这个数字,当最后结果为0时,则表示有一个结果。
代码可以是如下:
class Solution {
public:
int result=0;
int num=0;
void dfs(vector<int>& nums,int i)
{
if(i==nums.size() )
{
if( num==0)result++;
return ;
}
num+=nums[i];
dfs(nums,i+1);
num-=2*nums[i];
dfs(nums,i+1);
num+=nums[i];
}
bool canPartition(vector<int>& nums) {
dfs(nums,0);
return result;
}
};
2.动态规划
思路:
这题可以转换为背包问题。设所有的元素和为sum,如果sum为奇数,则不可能将这个数组分割成两个元素和相等的集合。如果是偶数,则可以变成背包问题,在这个数组里面挑选出若干元素,使得和为sum/2。
(1)定义状态
设dp[i][j]为在前i个元素里面使得和为j的可能性,如果可能则为true,否则为false。dp是二维数组,设数组内共有m个元素,num=sum/2。则dp的大小为(m+1)*(num+1)。
(2)状态转移
(1)当nums[i-1]>j时,则dp[i][j]=dp[i-1][j]。(此时表示不能将第i个元素加入子集)
(2)当nums[i-1]<=j时,则dp[i][j]=dp[ i-1 ] [ j ] | dp[ i-1 ] [ j-nums[i-1] ]。此时表示将不把第i个元素假如集合和把第i个元素加入集合的可能性做一个或运算。
(3)临界条件
(1)当i=0,j=0时,为true.因为使用前0个元素和为0的可能性就是不用子集。
当i=0,j>0时,为false.因为当前0个元素和大于0是不可能的。
(2)当i>0,j=0时,为true.因为前i个元素和为0时,就是不选元素。
按照上述,则我们可以得到如下代码:
class Solution {
public:
bool canPartition(vector<int>& nums) {
int sum=0;
for(int i=0;i<nums.size();i++)
{
sum+=nums[i];
}
if(sum%2!=0)return false;
sum=sum/2;
vector<vector<bool>>dp(nums.size()+1,vector<bool>(sum+1,0));
dp[0][0]=true;
for(int i=1;i<nums.size()+1;i++)
{
for(int j=0;j<sum+1;j++)
{
if (j >= nums[i - 1])
{//不选第i个和选第i个元素
dp[i][j] = dp[i - 1][j] | dp[i-1][j - nums[i - 1]];
}
else
{//不能选择第i个元素
dp[i][j] = dp[i - 1][j];
}
if(j==sum&&dp[i][j]==true)
{
return true;
}
}
}
return false;
}
};