0%

416. 分割等和子集

416.分割等和子集

题目描述:

给你一个 只包含正整数非空 数组 nums 。请你判断是否可以将这个数组分割成两个子集,使得两个子集的元素和相等。

数据范围:

$1\le nums.len \le 200; 1\le nums[i] \le 100$

题解:

总和为 sum,使用一个大小为 $\frac{sum}{2}$ 的背包装数字,最后返回装的价值是否等于容量。

也可以使用 $dp[i][j]$ 表示前 $i$ 个是否能够得到 $j$ 。初始时为 $dp[0][0] = 1, dp[0][j] = 0$ , $dp[i][j] = dp[i-1][j] | dp[i-1][j - nums[i]]$ 。一维为 $dp[j] |= dp[i - nums[i]]$

还有更高级的做法,使用 bitset。因为使用一维dp时,需要从后往前遍历,相当于对 $dp[j\cdots size] |= dp[0\cdots size-j]$ 。使用移位可以表示为 $dp |= dp << nums[i]$ 。相当于对 $dp[0,j-1]$ 或上了 $0$ ,对后面的或上了 $dp[j-nums[i]]$ 。

代码:

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
26
27
28
29
30
31
32
class Solution
{
public:
const static int maxn = 1e5 + 10;
const static int maxm = 1e5 + 10;
const int INF = 0x3f3f3f3f;
// dp[i][j] = max(dp[i-1][j], dp[i-1][j-nums[i]] )
int backet(vector<int> nums, int size)
{
int n = nums.size();
vector<int> dp(size + 1, 0);
for (int i = 0; i < n; ++i)
{
for (int j = 0; j <= size; j++)
{
dp[j] = max(dp[j], dp[j - nums[i]] + nums[i]);
}
}
return dp[size];
}
bool canPartition(vector<int> &nums)
{
int sum = 0;
for (auto &x : nums)
sum += x;
if (sum & 1)
return false;
if (backet(nums, sum / 2) == sum / 2)
return true;
return false;
}
};
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) {
int n = nums.size();
int sum = 0, maxx = 0;
for(auto& x: nums) {sum += x; maxx = max(maxx, x);}
if(sum & 1 || maxx > sum / 2) return false;
int size = sum / 2;
vector<bool> dp(size + 1, false);
dp[0] = true;
for(int i = 0; i < n; ++i)
{
for(int j = size; j >= nums[i]; --j)
{
dp[j] = dp[j] | dp[j - nums[i]];
}
}
return dp[size];
}
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Solution {
public:
bool canPartition(vector<int>& nums) {
int sum = 0;
for(int &num:nums) sum+=num;
if(sum&1) return false;
sum >>= 1;
bitset<10001> f; f[0]=1;
for(int i=0;i<nums.size();i++) {
f |= f<<nums[i];
}
//dp[j] = dp[j] | dp[j - nums[i]];
return f[sum];
}
};