解法一:动态规划 二维01背包
首先求和判断是否为偶数,如果和为奇数的话必不可能分成两个等和子集。然后对和除以2,得到的就是每个子集的元素之和 sum ,接下来就是一个类似01背包的问题。
记 dp[i][j] 表示在前 i 个数(数的索引从1开始)中任取,是否存在和为 j 的方案,存在则为 true ,不存在则为 false 。状态转移方程如下:
class Solution {public boolean canPartition(int[] nums) {i]int sum = 0;for (int i : nums) {sum += i;}if ((sum & 1) == 1) {return false;}sum /= 2;final int n = nums.length;boolean[][] dp = new boolean[n + 1][sum + 1];dp[0][0] = true;for (int i = 0; i < n; ++i) {for (int j = 1; j <= sum; ++j) {dp[i + 1][j] = dp[i][j];if (j >= nums[i]) {dp[i + 1][j] = dp[i + 1][j] || dp[i][j - nums[i]];}}}return dp[n][sum];}}
解法二:动态规划 一维01背包
注意到在解法一中, dp[i][:] 的状态仅和 dp[i-1][:] 相关,因此可以改写成一维的形式,但是要注意遍历的顺序要反转,防止提前覆盖前一轮的结果。
class Solution {public boolean canPartition(int[] nums) {int sum = 0;for (int i : nums) {sum += i;}if ((sum & 1) == 1) {return false;}sum /= 2;final int n = nums.length;boolean[] dp = new boolean[sum + 1];dp[0] = true;for (int i = 0; i < n; ++i) {for (int j = sum; j >= 1; --j) {if (j >= nums[i]) {dp[j] = dp[j] || dp[j - nums[i]];}}}return dp[sum];}}
