0%

416. Partition Equal Subset Sum

416. Partition Equal Subset Sum

若求是否存在两个子序列相等,只需要求序列中是否存在子序列和等于总序列的一半。所以当和为奇数时候就是无效的。

转化为dp

dp[i][j]表示0~i的数字能否获得和为j,若能为true,不能为false。

对于一行的dp[i][j]来说

  1. 取当前这个nums[i],则如果0~i-1的数字可以达到j - nums[i]和的话就是true,反之为false。也就是dp[i - 1][j - nums[i]]是否为true
  2. 不取当前这个nums[i - 1],则如果0~i-1可以达到和为j,也就是dp[i - 1][j]是否为true。

基础情况是当没有数字时,当且仅当j为0时为true。当和为0时,对于所有数字都是true,因此只需要不取。

二维dp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
class Solution {
public:
bool canPartition(vector<int>& nums) {
int sum = 0;
for(auto& num : nums)
sum += num;
if(sum & 1)
return false;
sum >>= 1;
vector<vector<bool>> dp(nums.size() + 1, vector<bool>(sum + 1, false));
dp[0][0] = true;
for(int i = 1; i <= sum; ++i)
dp[0][i] = false;
for(int i = 1; i <= nums.size(); ++i)
dp[i][0] = true;
for(int i = 1; i <= nums.size(); ++i)
{
for(int j = 1; j <= sum; ++j)
{
dp[i][j] = dp[i - 1][j] || (j >= nums[i - 1] ? dp[i - 1][j - nums[i - 1]] : false);
}
}
return dp.back()[sum];
}
};
dp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class Solution {
public:
bool canPartition(vector<int>& nums) {
int sum = 0;
for(auto& num : nums)
sum += num;
if(sum & 1)
return false;
sum >>= 1;
vector<bool> dp(sum + 1, false);
dp[0] = true;
for(int i = 1; i <= nums.size(); ++i)
{
for(int j = sum; j > 0; --j)
{
dp[j] = dp[j] || (j >= nums[i - 1] ? dp[j - nums[i - 1]] : false);
}
}
return dp.back();
}
};

此处从sum走是因为,i - num是一个小于 i 的值,可以保证走在后面的时候这个值还是上一行生成的结果,如果从小到大走,走到后面时就会把前面的结果刷新掉了

review

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class Solution {
public:
bool canPartition(vector<int>& nums) {
auto sum = accumulate(nums.begin(), nums.end(), 0);
if(sum & 1)
return false;
sum /= 2;
int m = nums.size();
vector<bool> dp(sum + 1, false);
dp[0] = true;
for(int i = 0; i < m; ++i)
{
for(int j = sum; j >= 0; --j)
dp[j] = dp[j] || (j >= nums[i] && dp[j - nums[i]]);
if(dp[sum])
return true;
}
return false;
}
};