题:https://leetcode.com/problems/partition-equal-subset-sum/
题目大意
数组nums中只有正数,求是否能将nums中的元素放到两个集合中,使得两个集合中的元素和相同。
思路
方法一 dfs
对于使用 dfs 分为 两个 集合,若其中有一个 成功返回 true
具体做法是 在递归的过程中 ,分别将 num 加入 不同的 集合中,若 有某一个 集合 成功,那么另一个就会成功。
但是 这个方法 在 java 中 会超时
这是由于 当 nums 从小到大排序时,target[i]>=nums[index] 这个条件 会递归到很多层后才会发生作用,也就是 剪枝在 整个树 的 底部。
我们改进的方法是 在nums中 从大到小访问数据,这样会 使得 target[i]>=nums[index] 条件更早发生。
超时版本
class Solution {
public boolean dfs(int[]nums,int index, int[] target){
for(int i = 0 ; i < 2 ; i++){
if(target[i]>=nums[index]){
target[i] = target[i] - nums[index];
if(target[i] == 0 || dfs(nums,index+1,target))
return true;
target[i] = target[i] + nums[index];
}
}
return false;
}
public boolean canPartition(int[] nums) {
int _sum = 0;
for(int num:nums)
_sum += num;
if((_sum&1) == 1)
return false;
_sum = _sum>>1;
int[] target = {_sum,_sum};
return dfs(nums,0,target);
}
}
Your runtime beats 98.40 % of java submissions.
甚至好于 dp
class Solution {
public boolean dfs(int[]nums,int index, int[] target){
for(int i = 0 ; i < 2 ; i++){
if(target[i]>=nums[index]){
target[i] = target[i] - nums[index];
if(target[i] == 0 || dfs(nums,index-1,target))
return true;
target[i] = target[i] + nums[index];
}
}
return false;
}
public boolean canPartition(int[] nums) {
int _sum = 0;
for(int num:nums)
_sum += num;
if((_sum&1) == 1)
return false;
_sum = _sum>>1;
Arrays.sort(nums);
int[] target = {_sum,_sum};
return dfs(nums,nums.length - 1,target);
}
}
方法二 0 -1 背包
前面为初始判断,
后面部分为非常 典型的0-1背包问题。
class Solution {
public boolean canPartition(int[] nums) {
int tSum = 0;
for(int num:nums)
tSum += num;
if((tSum&1) == 1)
return false;
tSum = tSum>>1;
boolean[][] dp = new boolean[nums.length+1][tSum+1];
dp[0][0] = true;
for(int i = 1 ; i <=nums.length;i++)
for(int j = 0 ;j <= tSum;j++)
if(j - nums[i-1] >= 0){
dp[i][j] = dp[i-1][j] | dp[i-1][j - nums[i-1]];
}else{
dp[i][j] = dp[i-1][j];
}
return dp[nums.length][tSum];
}
}
状态压缩
class Solution {
public boolean canPartition(int[] nums) {
int tSum = 0;
for(int num:nums)
tSum += num;
if((tSum&1) == 1)
return false;
tSum = tSum>>1;
boolean[] dp = new boolean[tSum+1];
dp[0] = true;
for(int num:nums)
for(int j = tSum ;j >= num;j--)
dp[j] |= dp[j - num];
return dp[tSum];
}
}