01背包问题之LeetCode1049java动态规划详解

10 篇文章 0 订阅
5 篇文章 0 订阅

思路详解

例子[2,7,4,1,8,1]中:
“组合 2 和 4,得到 2,所以数组转化为 [2,7,1,8,1],
组合 7 和 8,得到 1,所以数组转化为 [2,1,1,1],
组合 2 和 1,得到 1,所以数组转化为 [1,1,1],
组合 1 和 1,得到 0,所以数组转化为 [1],这就是最优值。

即 1 - ( ( 4 -2 ) - ( 8 - 7 ) ) ==> ( 2 + 8 +1 ) - ( 4 + 7 )
1)对于我们来说如果数组只有两个元素,这是最舒服的,因为此时答案就是大 - 小。从上面例子得到的最后等式,显然这道题是希望我们从数组中选出两堆数,使他们的和差值最小
2)在这里插入图片描述
3)假设这堆数位集合A,那么对于每个元素来说,都可以放进集合A或者不放进集合A,这不就跟01背包很相似了嘛?我们用一个 dp[i][j] 来表示前 i 个数,集合A的大小为 j 的时候,能达到的最大和。
(这里有点绕口,如果觉得我没有表述清晰可以debug一下!!)。那么对于每个元素:
如果不选择该元素(即该元素不放进集合A中),此时问题转换为"前 i -1 个数,集合A的大小为 j 的时候,能达到的最大和";
如果选择该元素,那么此时问题转换为"前 i - 1 个数,集合A的大小为 j - stones[i] 时,能达到的最大和"。

public int lastStoneWeightII(int[] stones) {
        if(stones==null || stones.length==0)
            return 0;
        int sum = 0;
        for(int stone:stones)
            sum += stone;
        int target = sum/2 + 1;
        int len = stones.length;
        //dp[i][j]:前i个数字,背包容量为j的最大价值
        int[][] dp = new int[len+1][target];
        for(int i=1;i<=len;i++){
            for(int j=1;j<target;j++){
                dp[i][j] = dp[i-1][j];
                //当第i个元素小于背包容量的时才可以考虑加入背包中
                //第i个元素如果不放进背包中,则dp[i][j] = dp[i-1][j]
                //如果放进背包中,则dp[i][j]=dp[i-1][j-stones[i-1]]+stones[i-1]
                if(stones[i-1] <= j)
                    dp[i][j] = Math.max(dp[i][j],dp[i-1][j-stones[i-1]]+stones[i-1]);
            }
        }
        //for(int[] arr:dp)
            //System.out.println(Arrays.toString(arr));
        return sum-2*dp[len][target-1];
    }

大家如果还有点小疑惑就一定要动手填表格!!这次填完印象就会更加深刻了!!
在这里插入图片描述

优化

我们仔细观察一下状态转移方程:dp[i][j] = Math.max(dp[i][j],dp[i-1][j-stones[i-1]]+stones[i-1]);

可以发现,每一次都只参照了上一行中的dp,因此我们可以把空间复杂度从二维降到一维。

public int lastStoneWeightII2(int[] stones){
        if(stones==null || stones.length==0)
            return 0;
        int sum = 0;
        for(int stone:stones)
            sum += stone;
        int target = sum/2+1;
        int len = stones.length;
        int[] dp = new int[target];
        for(int i=0;i<len;i++){
            //如果背包j的容量小于元素i 那么可以提前退出循环
            for(int j=target-1;j>=stones[i];j--){
                //dp[j]等价于dp[i-1][j]
                //dp[j-stones[i]]+stones[i]等价于dp[i-1][j-stones[i]]+stones[i]
                dp[j] = Math.max(dp[j],dp[j-stones[i]]+stones[i]);
            }
            //System.out.println(Arrays.toString(dp));
        }
        return sum-2*dp[target-1];
    }

如果博主表述的有哪些不清晰欢迎大家在评论区指出哦!如果有帮助的话还请点赞!!谢谢大家

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值