回溯 动态规划(0-1背包问题) 优化动态规划 416. 分割等和子集


416.分割等和子集

给定一个只包含正整数的非空数组。是否可以将这个数组分割成两个子集,使得两个子集的元素和相等。

注意:
每个数组中的元素不会超过 100
数组的大小不会超过 200

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/partition-equal-subset-sum

1、回溯 (会超时)

class Solution {
    public boolean canPartition(int[] nums) {//0-1背包问题
        int sum = 0;
        for (int v : nums)
            sum += v;
        if (sum % 2 != 0)
            return false;
        else{
            sum /= 2;
            return backtrace(nums,0,sum);
        }
    }
    //回溯 
    private boolean backtrace(int[] nums,int index,int sum){
        if (sum == 0)
            return true;
        if (sum < 0 || index == nums.length) 
            return false;
        
        
        boolean choose = backtrace(nums,index+1,sum);
        boolean unchoose = false;
        if (sum >= nums[index])
            unchoose = backtrace(nums,index+1,sum-nums[index]);
        return choose || unchoose;
    }
}

2、动态规划(0-1背包问题)

class Solution {
    //算法一:回溯(会超时)
    //算法二:动态规划(0-1背包问题)
    public boolean canPartition(int[] nums) {
        //status: 第一个集合元素和;选择:是否选入第一个集合
        
        //dp[i][j]=x; i表示前数组下标前i个数,j表示集合元素总和
        int sum = 0;
        int len = nums.length;
        for (int i = 0;i < len;i++)
            sum += nums[i];
        if (sum % 2 != 0)//
            return false;
        sum /= 2;

        boolean[][] dp = new boolean [len+1][sum+1];
        //base case
        for(int i = 0 ;i <= len;i++){
            dp[i][0] = true;//无论时前i个数,若集合元素和为0,全不选就是true
        }
        
        //status transfer
        for (int i = 1;i <= len;i++){//依次遍历元素
            for (int j = 1; j <= sum;j++){//第一个集合的元素和
                if (j >= nums[i-1]) 
                    dp[i][j] = dp[i-1][j-nums[i-1]] || dp[i-1][j];
                else
                    dp[i][j] = dp[i-1][j];
            }
        }
        return dp[len][sum];
    }
    
}

3、优化动态规划

dp[i][j] = dp[i-1][j-nums[i-1]] || dp[i-1][j];
注意看这个状态转移方程,发现每一次状态转移实际上只可能使用到dp[i-1][j] 或 dp[i-1][j-nums[i-1]],所以可以把代表数组下标 i的一个维度压缩,只表示 dp[j]

class Solution {
    //算法一:回溯(会超时)
    //算法二:动态规划(0-1背包问题)
    public boolean canPartition(int[] nums) {
        //status: 第一个集合元素和;选择:是否选入第一个集合
        
        //dp[j]=x; j表示集合元素总和
        int sum = 0;
        int len = nums.length;
        for (int i = 0;i < len;i++)
            sum += nums[i];
        if (sum % 2 != 0)//
            return false;
        sum /= 2;

        boolean[]dp = new boolean[sum+1];
        //base case
        dp[0] = true;//无论时前i个数,若集合元素和为0,全不选就是true
              
        //status transfer
        // Arrays.sort(nums);
        for (int i = 1;i <= len;i++){//依次遍历元素
            for (int j = sum; j >= 1;j--){//第一个集合的元素和,注意从j=sum 开始
                if (j >= nums[i-1]) 
                    dp[j] = dp[j-nums[i-1]] || dp[j];
                // else
                    // dp[j] = dp[j];
            }
        }
        return dp[sum];
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值