动态规划求解分割等和子集(java)leetcode416

目录

题目:

思路:

方法一:

方法二:

方法三:

方法四:


题目:

给你一个 只包含正整数 的 非空 数组 nums 。请你判断是否可以将这个数组分割成两个

子集,使得两个子集的元素和相等

思路:

把01背包问题套到本题上来。背包的体积为sum / 2;背包如果正好装满,说明找到了总和为 sum / 2 的子集。背包中每一个元素是不可重复放入。

然后用动态规划。

方法一:

dp[i][j]表示第0到i个元素中能否选到和等于j的元素。

递推的想法是dp[i][j]=dp[i-1][j] || dp[i-1][j-nums[i]];

class Solution {
    public boolean canPartition(int[] nums) {
        if(nums==null)return false;
        int len=nums.length;
        int sum=0;
        for(int i=0;i<len;i++){
            sum+=nums[i];
        }
        if(sum%2==1)return false;
        int target=sum/2;
        boolean[][]  dp=new boolean[len][target+1];
        //初始化
        for(int j=0;j<=target;j++){
            if(nums[0]==j){
                dp[0][j]=true;
            }else{
                dp[0][j]=false;
            }
        }

        //动态规划遍历
        for(int i=1;i<len;i++){
            for(int j=0;j<=target;j++){
                if(nums[i]>j){
                    dp[i][j]=dp[i-1][j];
                }else{
                    dp[i][j]=dp[i-1][j] || dp[i-1][j-nums[i]];
                }
            }
            if(dp[i][target])return true;
        }

        return dp[len-1][target];
    }
}
方法二:

法一基础上换为一维滚动优化

class Solution {
    public boolean canPartition(int[] nums) {
        if(nums==null)return false;
        int len=nums.length;
        int sum=0;
        for(int i=0;i<len;i++){
            sum+=nums[i];
        }
        if(sum%2==1)return false;
        int target=sum/2;
        boolean[] dp=new boolean[target+1];
        //初始化
        for(int j=0;j<=target;j++){
            if(nums[0]==j){
                dp[j]=true;
            }else{
                dp[j]=false;
            }
        }

        //动态规划遍历
        for(int i=1;i<len;i++){
            for(int j=target;j>=0;j--){
                if(nums[i]<j){
                    dp[j]=dp[j] || dp[j-nums[i]];
                }
            }
            if(dp[target])return true;
        }

        return dp[target];
    }
}
方法三:

dp[i][j]表示j容量背包下,第0到i个元素中选到的装进背包元素最大的和。(这个和肯定小于等于j,因为是在j容量下的)

递推的想法是dp[i][j]=max(dp[i-1][j] , dp[i-1][j-nums[i]]+nums[i]);

class Solution {
    public boolean canPartition(int[] nums) {
        if(nums==null)return false;
        int len=nums.length;
        int sum=0;
        for(int i=0;i<len;i++){
            sum+=nums[i];
        }
        if(sum%2==1)return false;
        int target=sum/2;
        int[][]  dp=new int[len][target+1];
        //初始化
        for(int j=nums[0];j<=target;j++){
            dp[0][j]=nums[0];
        }

        //动态规划遍历
        for(int i=1;i<len;i++){
            for(int j=0;j<=target;j++){
                if(nums[i]>j){
                    dp[i][j]=dp[i-1][j];
                }else{
                    dp[i][j]=Math.max(dp[i-1][j],dp[i-1][j-nums[i]]+nums[i]);
                }
            }
            if(dp[i][target]==target)return true;
        }

        return dp[len-1][target]==target;
    }
}
方法四:

法三基础上换为一维滚动优化

class Solution {
    public boolean canPartition(int[] nums) {
        if(nums==null)return false;
        int len=nums.length;
        int sum=0;
        for(int i=0;i<len;i++){
            sum+=nums[i];
        }
        if(sum%2==1)return false;
        int target=sum/2;
        int[] dp=new int[target+1];
        //初始化
        for(int j=nums[0];j<=target;j++){
            dp[j]=nums[0];
        }

        //动态规划遍历
        for(int i=1;i<len;i++){
            for(int j=target;j>=0;j--){
                if(nums[i]<j){
                    dp[j]=Math.max(dp[j],dp[j-nums[i]]+nums[i]);
                }
            }
            if(dp[target]==target)return true;
        }

        return dp[target]==target;
    }
}
  • 16
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值