Leetcode Algorithm 416. Partition Equal Subset Sum
Partition Equal Subset Sum
给定一个正整数数组,判断能不能分成两个和相等的子数组
解题思路
假如一个数组能够被分成两个和相等的子数组,那么这个数组的和sum
必定是偶数。
所以假如sum
是奇数的话,可以排除。假如sum
是偶数,那么每个子数组的和必定是sum/2
。
所以只需要找到数组里面某些元素的和能否达到sum/2
即可。
这样的话,这个问题就类似于0-1背包了,背包的容量总共是sum/2
。
我们用一个布尔数组dp[j]
来表示和为j
的子数组存在与否。
假设数组有n个元素,遍历数组的每个元素nums[i]
,可以通过下面的状态转移方程计算dp
的值:
dp[j] = dp[j] || dp[j - nums[i]]
, 其中
j∈[nums[i],sum/2]
,j
是递减的。
初始状态是dp[0] = true
,其余位置为false
。
举个例子说明:对于数组[1, 5, 11, 5]
,运算的过程是:
i / j | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 |
---|---|---|---|---|---|---|---|---|---|---|---|---|
init | T | F | F | F | F | F | F | F | F | F | F | F |
0 | T | T | F | F | F | F | F | F | F | F | F | F |
1 | T | T | F | F | F | T | T | F | F | F | F | F |
2 | T | T | F | F | F | T | T | F | F | F | F | T |
3 | T | T | F | F | F | T | T | F | F | F | T | T |
当然,我们也可才去early stop的策略,即当dp[sum/2]
这位置上是true的时候,就可以判断了。
代码
#include<iostream>
#include<vector>
#include<memory.h>
using namespace std;
class Solution {
public:
bool canPartition(vector<int>& nums) {
int sum = 0;
int n = nums.size();
for (int i = 0; i < n; i++) {
sum += nums[i];
}
if (sum % 2 == 1)
return false;
sum /= 2;
bool dp[sum + 1];
memset(dp, false, sum+1);
dp[0] = true;
for (int i = 0; i < n; i++) {
for (int j = sum; j >= nums[i]; j--) {
dp[j] = dp[j] || dp[j - nums[i]];
if (dp[sum])
return true;
}
}
return false;
}
};
struct Testcase {
vector<int> nums;
void readInput() {
int n;
cin >> n;
for (int i = 0; i < n; i++) {
int num;
cin >> num;
nums.push_back(num);
}
}
};
int main() {
Solution s;
int t = 2;
Testcase tcase[t];
for (int i = 0; i < t; i++) {
tcase[i].readInput();
cout << s.canPartition(tcase[i].nums) << endl;
}
return 0;
}
测试样例
每一行第一个数字是数组大小,随后是数组的元素。
4 1 5 11 5
4 1 2 3 5
输出
1
0