leetcode: 416. 分割等和子集
题目描述
给你一个 只包含正整数 的 非空 数组 nums 。请你判断是否可以将这个数组分割成两个子集,使得两个子集的元素和相等。
示例
输入: nums = [1,5,11,5]
输出: true
解释: 数组可以分割成 [1, 5, 5] 和 [11] 。
解题思路
问题要求判断数组能否分为两个总和相同的子集,那么就可以转换为是否存在子集满足总和为数组总和的一半。
01背包即背包中的每件物品只能使用一次。
二维解法
容易想到,用dp[i][j]表示使用前i件物品,总价值不超过j的最大价值。
那么,dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - v] + v)。
- 不使用当前物品
- 使用当前物品(判断
j值)
一维优化
将dp[i][j]压缩到一维dp[j],更新dp[j] = min(dp[j], dp[j - v] + v)
- 外循环仍然是前
i件物品 - 内循环
j从大到小- 当前遇到
dp[j]实际为dp[i - 1][j],即判断的是min(dp[i - 1][j], dp[i - 1][j - v] + v)。 - 如果从小到大
dp[j] (dp[i - 1][j])会被提前更新为dp[i][j],即判断的是min(dp[i - 1][j], dp[i][j - v] + v)。
- 当前遇到
完整代码:
class Solution {
public boolean canPartition(int[] nums) {
int sum = 0;
for (int num : nums) {
sum += num;
}
if (1 == sum % 2) return false;
int target = sum / 2;
boolean[] dp = new boolean[target + 1];
dp[0] = true;
for (int i = 1; i <= nums.length; i++) {
int val = nums[i - 1];
for (int j = target; j >= val; j--) {
if (!dp[j]) dp[j] = dp[j - val];
}
if (dp[target]) return true;
}
return false;
}
}