力扣416 动态规划算法

此题可以转化成01背包问题,把数组的数组分成两部分,使两部分的值相等,各位数组值总和sum的一半,就是说从这个数组能不能凑出来一组数据的和为sum的一半,如果可以那么剩下来的值就可以凑出来另外一半。凑出一半总和的想法就是01背包了

class Solution {
    public boolean canPartition(int[] nums) {
        /**
            1、确定状态与选择
                i:代表物品的重量,也就是数组中的数值
                w:背包的容量,从0到数组总和一半的值
            2、定义dp数组
                 boolean[][] dp
                 dp[i][j] :前n物品可以放满背包容量为j时值为true
                 最终返回的是dp[nums.length][sum/2]
            3、初始化条件
                dp[i][0] = true
                dp[0][j] = false;
            4、状态转移逻辑
                对于第i个物品
                如果nums[i] > w ,即物品重量大于背包容量
                    不能放入背包,dp[i][j] = dp[i-1][j]
                否则,说明该物品可以放入背包,那么有两种选择放或者不放,
                    dp[i][j] = dp[i][j-1] || dp[i-1][j- nums[i]]
         */ 
         int n = nums.length;
        int sum = 0;
        int max = 0;
        for(int i =0;i<nums.length;i++){
            max = Math.max(max,nums[i]);
            sum += nums[i];
        }
        if(sum%2!=0){
            return false;
        }
        if(max > sum/2){
            return false;
        }
        sum /=2;
        //定义dp数组
        boolean[][] dp = new boolean[n+1][sum+1];
        //初始化
        
        for(int i=0;i<=sum;i++){
            dp[0][i] =false;
        }
        for(int i=0;i<=n;i++){
            dp[i][0] =true;
        }
        
        for(int i = 1;i<=n;i++){
            for(int j =1;j<=sum;j++){
                if(nums[i-1] > j){
                    dp[i][j] = dp[i-1][j];
                }else {
                    dp[i][j] = dp[i-1][j] || dp[i-1][j-nums[i-1]];
                }
            }
        }
        return dp[n][sum];
    }
}

一下是改造之后状态压缩的代码,用一个一维数组替代二维,因为在状态转移是被使用的总是当前行和上一行数据,所以从数组后面遍历,不断地向前覆盖数据即可,这样既节省了空间,又避免了数据冲突

class Solution {
    public boolean canPartition(int[] nums) {
        /**
            1、确定状态与选择
                i:代表物品的价值,也就是数组中的数值
                w:背包的容量,从0到数组总和一半的值
            2、定义dp数组
                 boolean[][] dp
                 dp[i][j] :前n物品可以放满背包容量为j时值为true
                 最终返回的是dp[nums.length][sum/2]
            3、初始化条件
                dp[i][0] = true
                dp[0][j] = false;
            4、状态转移逻辑
                对于第i个物品
                如果nums[i] > w ,即物品重量大于背包容量
                    不能放入背包,dp[i][j] = dp[i-1][j]
                否则,说明该物品可以放入背包,那么有两种选择放或者不放,
                    dp[i][j] = dp[i][j-1] || dp[i-1][j- nums[i]]
         */ 
         int n = nums.length;
        int sum = 0;
        int max = 0;
        for(int i =0;i<nums.length;i++){
            max = Math.max(max,nums[i]);
            sum += nums[i];
        }
        if(sum%2!=0){
            return false;
        }
        if(max > sum/2){
            return false;
        }
        sum /=2;
        //定义dp数组
        boolean[] dp  = new boolean[sum+1];
        // boolean[][] dp = new boolean[n+1][sum+1];
        //初始化
        
        // for(int i=0;i<=sum;i++){
        //     dp[0][i] =false;
        // }
        // for(int i=0;i<=n;i++){
        //     dp[i][0] =true;
        // }
        dp[0] = true;
        for(int i = 1;i<=n;i++){
            for(int j =sum;j>0;j--){
                // if(nums[i-1] > j){
                //     dp[i][j] = dp[i-1][j];
                // }else {
                //     dp[i][j] = dp[i-1][j] || dp[i-1][j-nums[i-1]];
                // }
                if(nums[i-1] <= j){
                     dp[j] = dp[j] || dp[j-nums[i-1]];
                }
            }
        }
        return dp[sum];
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值