一、题目
给你一个 只包含正整数 的 非空 数组 nums
。请你判断是否可以将这个数组分割成两个子集,使得两个子集的元素和相等。
示例 1:
输入:nums = [1,5,11,5] 输出:true 解释:数组可以分割成 [1, 5, 5] 和 [11] 。
示例 2:
输入:nums = [1,2,3,5] 输出:false 解释:数组不能分割成两个元素和相等的子集。
提示:
1 <= nums.length <= 200
1 <= nums[i] <= 100
二、代码
class Solution {
public boolean canPartition(int[] nums) {
// 计算整个数组的累加和
int sum = 0;
for (int num : nums) {
sum += num;
}
// 如果累加和是奇数,那么不可能平分,返回false
if ((sum & 1) != 0) {
return false;
}
// 将累加和除以二
sum >>= 1;
// dp[i][rest]:表示来到i位置尝试,还剩下rest没有凑出来,这种情况能否凑出来整个数组累加和的一半
// 1 true
// -1 false
// 0 还未赋值
int[][] dp = new int[nums.length + 1][sum + 1];
// 递归求解,返回能不能在nums数组中找到一个集合可以凑出来累加和的一半
return process(sum, nums, 0, dp);
}
// 当前还剩下rest没有凑出,此时已经尝试到i位置了
public boolean process(int rest, int[] nums, int i, int[][] dp) {
// 如果当前情况已经计算过了,直接返回答案
if (dp[i][rest] != 0) {
return dp[i][rest] == 1;
}
// 如果剩余为0,说明凑出来了,返回true
if (rest == 0) {
// 赋值
dp[i][rest] = 1;
return true;
// 剩余没凑出来的不为0
} else {
// 如果此时已经把所有的情况都尝试过了,还没有凑出数组累加和的一半,说明无法凑出,返回false
if (i == nums.length) {
// 赋值
dp[i][rest] = -1;
return false;
// 还没有遍历完,继续尝试
} else {
// 情况一:不选择i位置的数,直接去下个位置做选择
boolean p1 = process(rest, nums, i + 1, dp);
// 情况二:选择i位置的数,剩余没凑出来的数就是rest - nums[i]
boolean p2 = false;
// 需要帮正选择nums[i]后,凑出来的数不能超过rest
if (rest - nums[i] >= 0) {
p2 = process(rest - nums[i], nums, i + 1, dp);
}
// 只要是两种情况有能成的,就返回true
if (p1 || p2) {
// 赋值
dp[i][rest] = 1;
return true;
} else {
// 赋值
dp[i][rest] = -1;
return false;
}
}
}
}
}
三、解题思路
就是一道背包问题,详细见注释。