01背包问题——动态规划详解

40 篇文章 1 订阅
21 篇文章 1 订阅

        有N件物品和⼀个最多能被重量为W的背包。第i件物品的重量是weight[i],得到的价值是value[i]。件物品只能⽤⼀次,求解将哪些物品装入背包⾥物品价值总和最⼤。

        在题目中我们假设有三件物品

                        重量        价值

        物品1        1                15

        物品2        3                20

        物品3        4                30

        那么假设背包的最大承重量给定为最重物品的重量,求解可以在不超重情况下背包存放的最大价值

        01背包问题是动态规划算法中最基础的也是最为重要的一类问题,其解题过程比较抽象,数组的定义与爬楼梯等问题相比,还是难以理解。下面我们就来探索一下这个问题的解法。

        首先题目的例子不难,我们来猜测一下,重量为4的背包放以上三件物品,那么有多种放法,1、2、3、1和2四种放法,其中1和2最贵,那么本题的答案就是35。下面我们来理一理思路。

        为什么我们舍弃了单放一个3而选择了1和2?因为1和2比3贵嘛!好吧,这里是正常人的思维,下面我们来打开思维。假设背包最大承重为3,我们有两种放法,一个是1,一个是2,为什么我们选择2?因为我们首先选择了1,当选2的时候发现背包容量不够,所以我们就有了取出物品1的想法。当我们取出物品1,放入物品2之后我们就认定了最贵就是单放物品2了吗?显然不是,我们把取出物品1后放入物品2的总价值没取出物品1的背包物品总价值进行了对比,发现原来把1拿出来后放2,比不放2价值更大,所以选择了后者。

        现在回过来看,为什么我们选择物品1和物品2而不是单单一个物品3呢?因为我们取出物品1和2后放入物品3的总价值低于不放物品3的总价值所以我们没有取出物品。这就是核心的递推公式呀!!!

递推公式

        当前承重量的背包最大容纳价值 = max(腾出新物品空位后的背包承重量最大价值 + 新物品的价值,不加入新物品的最大价值)

        

定义一个二维数组,每一列为从0-max解放的背包重量,每一行表示新解放的物品

当只能放物品1时,背包容量从0-max,每个容量对应的最大价值就为
              0  1   2   3   4
        15  0 15 15 15 15
        20
        30

当解放物品2,那么背包容量从0-max,每个容量对应的最大价值就为

              0  1   2   3   4
        15  0 15 15 15 15
        20  0 15 15 20 35
        30  

当解放物品2,那么背包容量从0-max,每个容量对应的最大价值就为

              0  1   2   3   4
        15  0 15 15 15 15
        20  0 15 15 20 35
        30  0 15 15 20 35

例1:以第三行第四个的价值35为例

35 = max(不加入第三个物品的最大价值(第二行第四列),去除加入物品重量后的最大价值(第二行第一列) + 新物品价值)

public class Bag01 {
    public static void main(String[] args) {
        int[] weight = new int[]{1, 3, 4};
        int[] value = new int[]{15, 20, 30};
        int i = maxValue(weight, value);
        System.out.println(i);

    }

    public static int maxValue(int[] weight, int[] value) {

        //根据weight确定二维数组的行宽,数组的最后一个元素索引为最大重量
        //根据背包中物品的种类确定二维数组的列宽
        int[][] realValue = new int[weight.length][weight[weight.length - 1] + 1];

//            0  1  2  3  4
//        15  0 15 15 15 15
//        20
//        30

        //当重量为0,1,2,3,4时,背包只能存放物品1的情况下,最大价值为
        for (int i = weight[weight.length - 1]; i >= 0; i--) {
            //如果该背包容量能存放下的话,那么就存入第一个物品
            if (i >= weight[0]) {
                realValue[0][i] = value[0];
            }
        }

        //当重量为0时,背包无法存放物品
        for (int i = 0; i < realValue.length; i++) {
            realValue[i][0] = 0;
        }

        //初始化内容
        //数组的列表示物品的种类,数组的的横向表示背包的承重量
        //数组的第一层就表示 达到上限前 背包的最大沉重量 依次可以存放的 最贵物品的 总价值
        //数组的第一列就表示 当重量为0时,背包无法存放物品,最大价值为0

        //观察第二层数据
        //当解放第二个物品时
//            0  1  2  3  4
//        15  0 15 15 15 15
//        20  0 15 15 20 35
//        30  0
        //重量为0,1,2时,背包都放不下第二个物品,所以最大价值继承没解放第二个物品时的价值
        //重量为3时,背包的可以容纳进第二个物品,那么比较:
        //      如果不加入第二个物品,那么最后的价格 就保留未解放第二个物品时 该承重的最大价格
        //      如果加入第二个物品,那么最后的价格 就是除去该物品重量后背包承重的最大价格 + 第二个物品的价格
        //  很显然,20 + 0 > 15 所以最大价格为20
        //重量为4时,重复重量为3的操作 20 + 15 > 30,所以写入35

        //从第二行开始
        for (int i = 1; i < realValue.length; i++) {
            //从第二列开始
            for (int j = 1; j < realValue[0].length; j++) {
                //如果新加入物品的重量小于最大承重
                if (weight[i] <= j) {
                    //比较
                    realValue[i][j] = Math.max(realValue[i - 1][j], realValue[i - 1][j - weight[i]] + value[i]);
                }
                //如果新加入物品的重量大于最大承重
                else {
                    realValue[i][j] = realValue[i - 1][j];
                }
            }
        }

        //遍历一下数组看看长啥样

        for (int[] ints : realValue) {
            for (int i = 0; i < ints.length; i++) {
                System.out.print(ints[i] + " ");
            }
            System.out.println();
        }


        //返回数组右下角的最终值,即解放所有物品所有重量后的值
        return realValue[realValue.length - 1][realValue[0].length - 1];
    }
}

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Aristocrat l

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值