题目描述
给定一个只包含正整数的非空数组。是否可以将这个数组分割成两个子集,使得两个子集的元素和相等。
注意:
- 每个数组中的元素不会超过 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)
- 转移方程:
[egin{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];
}
};