LeetCode 416. 分割等和子集
题目描述
给定一个只包含正整数的非空数组。是否可以将这个数组分割成两个子集,使得两个子集的元素和相等。
注意:
- 每个数组中的元素不会超过 100
- 数组的大小不会超过 200
示例1:
输入: [1, 5, 11, 5]
输出: true
解释: 数组可以分割成 [1, 5, 5] 和 [11].
示例2:
输入: [1, 2, 3, 5]
输出: false
解释: 数组不能分割成两个元素和相等的子集.
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/partition-equal-subset-sum
思路解析
- 遍历数组
nums
,计算得到数组和为\(S\),最大值为\(m\); - 若\(S\)为奇数,或\(m > S / 2\)`,返回
false
,记\(target = S / 2\); - 记\(f(i, j)\)表示区间\([0,i]\)内选择有限个数字,其和能否为\(j\),若能,则\(f(i, j) = T\),否则\(f(i, j) = F\),其中\(i \in [0, n - 1], j \in [0, target]\);
- 易知:\(f(i,0) = T\),\(f(0,nums[j]) = T\)
- 转移方程:
\[\begin{align*}
& f(i,j) = f(i - 1, j) || f(i - 1, j - nums[i])\quad (j >= nums[i]) \\
& f(i,j) = f(i - 1, j)\quad (j < nums[i])
\end{align*}
\]
代码实现
class Solution {
public:
bool canPartition(vector<int>& nums) {
if(nums.size() < 2)
return false;
int sum = 0;
int max = 0;
for(auto num : nums) {
sum += num;
max = (max > num) ? max : num;
}
if(sum % 2)
return false;
int target = sum / 2;
if(max > target)
return false;
int imax = nums.size();
int jmax = target + 1;
vector<vector<bool>> subs(imax, vector<bool>(jmax, false));
for(int i = 0; i < imax; i++)
subs[i][0] = true;
subs[0][nums[0]] = true;
for(int j = 1; j < jmax; j++) {
for(int i = 1; i < imax; i++) {
if(j < nums[i])
subs[i][j] = subs[i - 1][j];
else {
subs[i][j] = (subs[i - 1][j - nums[i]] || subs[i - 1][j]);
}
}
}
return subs[imax - 1][jmax - 1];
}
};