动态规划算法解决背包问题(Java实现)

2023-11-18

文章收藏的好句子:你在书本上花的任何时间,都会在某一个时刻给你回报。

目录

1、动态规划算法的概述

2、背包问题

3、动态规划算法解决背包问题

     3、1 不可重复装入商品

     3、2 思路分析

1、动态规划算法的概述

(1)动态规划算法的思想是:将大问题分为小问题进行解决,使得一步步获得最优解的处理算法。

(2)动态规范算法与分治算法很类似,思想都是以待解决问题先分解成 n 个子问题,先求解子问题,然后从子问题中得到原问题的解。

(3)动态规划求解的问题与分治算法不同的点在于,经过分解出来的子问题不是相互独立的,下一个子问题的求解是建立在上一个子问题解决的求解基础上的,依次递进获取最终解。

(4)动态规划可以通过填表的方式一步步推进,最终得到最优解。

2、背包问题

这里就有一个背包问题了,有一个容量为4磅的背包,需要装入如下表1的物品,怎样才能使装入包中的商品最值钱且不超过4磅重?

b67a4f1e53118a6d81f12fc432316a8b.png

3、动态规划算法解决背包问题

上面的背包问题,放入商品的总质量不能超过4磅且要实现背包装入商品价值的最大化,这里假设放入的商品是不能重复的, 可用一个二维表格来表示。

3、1 不可重复装入商品

我先画一个表格2,然后会对这个表格2进行详细的说明,如下所示;

5b417a7165e108668904cf26d16313aa.png

说明:列表示背包能装多大的重量,横表示物品的类型和商品数量,行和列交叉而出现的数据表示背包能装的物品总和的最大价值。

好,我们现在对表2的数据分析一下,第一行的数据全部为0,这个可以理解吧?不管背包能放多大的重量,只要不放入物品,那就是0咯;第一列的数据全部为0,是因为背包能装0磅;我们看第二行第二列的数据到第二行第五列的数据,首先第二行能放的物品只能是鞋子且不能重复对不对?那不管背包(装的重量大于等于1磅)能装多少磅的物品,都是只能放1磅的鞋子对不对?那自然就是1500了。

我们看第三行第二列到第三行第五列的数据,第三行能放入的物品是鞋子、音响且同一个物品不能放超过1个,第三行的第二列到第三行的第四列只能放1磅的鞋子,放不下音响(音响4磅重),所以就是1500了,而第三行的第五列刚好能放4磅的音响,而且放一个音响比放一双鞋子更值钱对不对?

第四行可以放的商品有鞋子、音响和电脑,好,现在看第四行的第二列到第四行的第三列,由于第二列和第三列是只能装1磅和2磅对不对?那就只能放一双鞋子了,所以就是1500了;第四行的第四列能装3磅,那就是可以放一双鞋子或者一台电脑,由于电脑比鞋子更值钱对不对,所以放电脑更好点,所以就是2000了;看第四行的第五列,背包可以装4磅,那这里面就有较好一点的两种方案选择了,一种是放一双鞋子和一台电脑,另一种是只放一台音响,一看放一双鞋子和一台电脑的方案更值钱对不对,所以是1500+2000就是3500了。

从表2可以小结一下

(1)横行表示背包容量从0到指定容量的各种情况,这是第一步的分,将大容量的背包先转化为小容量背包,算出子问题的最优解,然后一步步加大容量,算出最终问题的最优解。

(2)纵行表示商品信息,且第一横行为空值,作为初始数据的对比值;纵行是第二步的分,先将一个商品放入背包中,算出最优解,逐渐增加商品类型和商品数量,算出最终最优解。

(3)最终表格的最右下角的格子,即为数据的最优解;看表2最右下角的数据3500,是不是最优解。

3、2 思路分析

(1)利用动态规划来解决,假设给定的 n 个物品,设 v[i]、 w[i] 分 别为第 i 个商品的价值和重量,C 为背包的容量。

(2)每次遍历到的第 i 个商品,根据 w[i] 和 v[i] 来确定是否需要将该商品放入背包中;这句话说的是什么意思呢?我举个例子,你们就理解了,看表2的第四行的第四列的2000这个数据,首先第四列背包最大容量是3磅对不对?第四行能放的商品有鞋子、音响和电脑对不对?但是音响比背包的容量更大,所以就只能放鞋子和电脑,鞋子和电话的重量和超过3磅对不对,所以又只能从鞋子和电脑里面挑选一个放进去,由于电脑比鞋子更值钱对不对?所以放电脑价值更大对不对?所以是根据 w[i] 和 v[i] 来确定是否需要将该商品放入背包中。

(3)再令 v[i][j] 表示在前 i 个商品中能够装入容量为 j 的背包中的最大价值;这句话又是什么意思啊?我再举个例子,看表2的第三行第五列的3000,这时候 i 就是2,j 就是4,v[i][j] 就是 v[2][4],也就是 v[2][4] 为3000;第二行只能装的商品是鞋子对不对?第三行能装入的商品包含第二行装入的商品,也就是说第三行能选择装入的商品是鞋子和音响;如果鞋子和音响同时放入背包(背包容量为4磅,j=4)肯定是装不下的对不对?所以从鞋子和音响里面选最值钱的放入背包中,所以就是 v[i][j] 表示在前 i 个商品中能够装入容量为 j 的背包中的最大价值。

现在我们从思路分析和表2中能够总结出以下几条公式;

(1)v[i][0] = v[0][j] = 0,其中 i 表示第几行,j 表示第几列;看表2,第一列的数据是不是0?第一行的数据是不是也是0?

(2)当 w[i] > j 时,有 v[i][j] = v[i-1][j],w 表示第 i+1 行商品的重量 ;举例:看表2中的第三行的第二列,i = 2,w[i] = 4,j = 1,v[2][1] = v[2-1][1]  = 1500 。

(3)当j >= w[i] 时,有 v[i][j]=max{v[i-1][j],v[i-1][j-w[i]]+v[i]} ,v[i] 表示第 i+1 行商品的价格;举例:看表2,看第四行的第五列数据,j = 4,i = 3,w[3] = 3,v[3] = 2000,那么 v[3][4] = max{v[3-1][4] , v[3-1][4-w[3]]+v[3]} = max{3000 , 3500},所以 v[3][4] = 3500 。

好,我们现在用代码实现一把;

(1)新建一个 Test 类:

package com.xiaoer.demo;


/**
 * 动态规划: 背包问题
 **/
public class Test {


    public static void main(String[] args) {
      Test test = new Test();
      
        // 商品名字数组
        String[] nameArr = {"鞋子", "音响", "电脑"};
        
        // 商品重量数组
        int[] weightArr = {1/*鞋子*/, 4/*音响*/, 3/*电脑*/};
        
        // 商品价格数组
        int[] priceArr = {1500/*鞋子*/, 3000/*音响*/, 2000/*电脑*/};
        
        // 背包容量
        int packageCapacity = 4;
        
        // 把不可重复的商品装入背包
        test.backpackWithoutRepeat(nameArr, weightArr, priceArr, packageCapacity);
    }




    /**
     * 装入背包
     * @param nameArr 商品名字数组
     * @param w 商品重量数组
     * @param priceArr 商品价格数组
     * @param packageCapacity 背包容量
     */
    private void backpackWithoutRepeat(String[] nameArr, int[] w, int[] priceArr, int packageCapacity) {
        /**
         * 声明一个能装入 0、1、2、3磅......的背包的二维价格表;举例:就好比 v数组是表2的数据
         */
        int[][] v = new int[nameArr.length + 1][packageCapacity + 1];
        
        // 构建可能装入背包的二维数组
        // 值为0时说明不会装进背包, 值为1说明可能装入背包
        int[][] contentArr = new int[nameArr.length + 1][packageCapacity + 1];
        
        /**
         * 为什么i一开始是1不是0?看表2的数据,是不是第一行全是0啊
         */
        for (int i = 1; i < v.length; i++) {
            
          /**
           * 为什么j一开始是1不是0?看表2的数据,是不是第一列全是0啊
           */
            for (int j = 1; j < v[i].length; j++) {
                
              /**
               * 文章中当 w[i] > j 时,就有 v[i][j] = v[i-1][j];
               * 因为我们程序i是从1开始的,因此原来公式中的w[i]修改成w[i-1];
               * 当前商品 > 背包容量, 取同列上一行数据
               */
                if (w[i - 1] > j) {
                    v[i][j] = v[i - 1][j];
                } else {
                    /**
                     *  当前商品 <= 背包容量, 对两部分内容进行比较;
                     *  第一部分, 该列上一行数据
                     */
                    int onePart = v[i - 1][j];
                    
                    /**
                     * 还记得文章中写的 当j >= w[i] 时,有 v[i][j]=max{v[i-1][j],v[i-1][j-w[i]]+v[i]} 这个公式成立吗?
                     * priceArr[i - 1]: 当前商品价格;
                     * w[i - 1]: 当前商品重量;
                     * j - w[i - 1]: 去掉当前商品, 背包剩余容量;
                     * 不可重复: v[i - 1][j - w[i - 1]]: 在上一行, 取剩余重量下的价格最优解;
                     */
                    int otherPart = priceArr[i - 1] + v[i - 1][j - w[i - 1]];
                    
                    /**
                     *  取最大值为当前位置的最优解
                     */
                    v[i][j] = Math.max(onePart, otherPart);
                    
                    /**
                     *  如果最优解包含当前商品, 则表示当前商品已经被使用, 进行记录
                     */
                    if (otherPart == v[i][j]) {
                        contentArr[i][j] = 1;
                    }
                }
            }
        }


        // 不能重复的场景中
        // 如果该位置的标志位为1, 说明该商品参与了最终的背包添加
        // 如果该位置的标志位为0, 即使该位置的价格为最大价格, 也是从其他位置引用的价格
        // 因为不能重复, 所以每行只取一个数据参与最终计算, 并只判断在最大位置该商品是否参与
        // 该最大位置会随着已经遍历出其他元素而对应不断减小, 直到为0


        // 二维数组最后一个元素必然是最大值, 但是需要知道该最大值是自身计算的 还是比较后引用其他的
        int totalPrice = 0;
        // 最大行下标数, 即商品数
        int maxLine = contentArr.length - 1;
        // 最大列下标数, 即重量
        int maxColumn = contentArr[0].length - 1;
        for (;maxLine > 0 && maxColumn > 0;) {
            // 等于1表示在该位置该商品参与了计算
            if (contentArr[maxLine][maxColumn] == 1) {
                // 遍历后, 对重量减少, 下一次从剩余重量中取参与商品
                maxColumn -= w[maxLine - 1];
                totalPrice += priceArr[maxLine - 1];
                System.out.println(nameArr[maxLine - 1] + "加入了背包");
            }
            // 因为不能重复
            // 所以如果该商品参与了背包容量, 则肯定剩余的最大位置处参与,
            // 否则跟该数据无关, 直接跳过
            maxLine--;
        }
        System.out.println("背包可容纳的最大价值: " + totalPrice);
    }


}

程序运行结果如下所示;

1acecf106358b219df81626f2867a326.png

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

动态规划算法解决背包问题(Java实现) 的相关文章

随机推荐

  • Vue3之路--Less教学

    概览 Less Leaner Style Sheets 的缩写 是一门向后兼容的 CSS 扩展语言 这里呈现的是 Less 的官方文档 中文版 包含了 Less 语言以及利用 JavaScript 开发的用于将 Less 样式转换成 CSS
  • 关于table的selectedRowKeys和selectedRows

    项目使用的组件库是antd 页面中有很多table 有的table有行前面的复选框 于是就有了selectedRowkeys和selectedRows的事 他们两个都是数组 selectedRowkeys存的是table的rowKey 也就
  • .Net/C#: 实现支持断点续传多线程下载的 Http Web 客户端工具类 (C# DIY HttpWebClient)

    选择自 playyuer 的 Blog Net C 实现支持断点续传多线程下载的 Http Web 客户端工具类 C DIY HttpWebClient Reflector 了一下 System Net WebClient 重载或增加了若干
  • 论文阅读-NOLANet多模态伪造检测

    一 论文信息 题目 Deepfake Video Detection Based on Spatial Spectral and Temporal Inconsistencies UsingMultimodal Deep Learning
  • SQL学习笔记——limit用法(limit使用一个参数,limit使用两个参数)

    Product表 limit语法 select lt 列名 gt lt 列名 gt from lt 表名 gt limit lt 参数值 gt select from product limit 3 product id product n
  • 第三方平台代微信公众号开发

    第三方平台代微信公众号开发流程 一 准备工作 微信开放平台相关 申请微信开放平台账号后 需前往微信开放平台 创建第三方平台 填写开发相关配置 填写授权流程相关配置 注意事项 授权发起页域名 为项目开发使用域名 调用公众号二维码授权页时 必须
  • test is not a function (js正则表达式匹配问题)

    js中正则表达式匹配时 如果使用test函数 就必须不带引号 并且必须是 定义的规则变量 test 要测试的string 定义变量规则不要带引号 会错误的 如果不使用test 使用match则可以带引号 var re 1 9 d 4 10
  • Android 组件逻辑漏洞漫谈

    前言 随着社会越来越重视安全性 各种防御性编程或者漏洞缓解措施逐渐被加到了操作系统中 比如代码签名 指针签名 地址随机化 隔离堆等等 许多常见的内存破坏漏洞在这些缓解措施之下往往很难进行稳定的利用 因此 攻击者们的目光也逐渐更多地投入到逻辑
  • QT4信号连接与QT5的区别

    QT4信号连接与QT5的区别 QT4信号与槽 1 申明槽函数必须增加public slots 2 SIGNAL SLOT 将函数转为字符串 不进行错误检查 connect中信号和槽需要增加SIGNAL 和SLOT 3 槽函数和信号一致 参数
  • 常用的表格正则验证 + 省份选择 JS JQ

    常用的表格正则验证 轮子 let receiverNameReg u4e00 u9fa5 2 6 reg 收货人姓名 let receiverName receiverName val 收货人姓名 let phoneNumberReg d
  • TCP的几个状态 SYN, FIN, ACK, PSH, RST, URG

    2019独角兽企业重金招聘Python工程师标准 gt gt gt TCP的几个状态对于我们分析所起的作用 在TCP层 有个FLAGS字段 这个字段有以下几个标识 SYN FIN ACK PSH RST URG 其中 对于我们日常的分析有用
  • 数据挖掘技术-绘制散点图

    绘制散点图 前置步骤 准备数据guomin npz 下载数据guomin npz到Linux本地的 course DataAnalyze data目录 绘制散点图 绘制2000 2017年各季度的国民生产总值散点图 如代码 41所示 代码
  • 【华为OD机试真题 JAVA】执行时长

    JS版 华为OD机试真题 JS 执行时长 标题 执行时长 时间限制 1秒 内存限制 262144K 语言限制 不限 为了充分发挥GPU算力 需要尽可能多的将任务交给GPU执行 现在有一个任务数组 数组元素表示在这1秒内新增的任务个数且每秒都
  • Python脚本报错AttributeError: ‘module’ object has no attribute’xxx’解决方法

    最近在编写Python脚本过程中遇到一个问题比较奇怪 Python脚本完全正常没问题 但执行总报错 AttributeError module object has no attribute xxx 这其实是 pyc文件存在问题 问题定位
  • #C++矩阵类的实现

    C 矩阵类的实现 环境 Win10 VS2017 最近老师布置一个简单的C 作业 实现一个矩阵类 并且实现矩阵运算 主要实现运算为矩阵的加 减 乘 除以及求行列式 伴随矩阵 代数余子式和逆矩阵等 在参考网上的一些前辈的代码后 写出了这些运算
  • 信号与系统复习题

    选择题 2分 题 1 频谱与时域的关系 时域压缩 频域展宽 时域有限 频域无限 2 填空题 20分 2分 空 1 冲击信号的性质 抽样性 尺度变换性 奇偶性 2 线性时不变的概念 线性 齐次性 输入夸大多少倍 输出扩大多少倍 可加性 相应的
  • HFP协议

    通话专题HFP协议学习总结 一 配置和角色 二 HFP的连接 2 1服务级连接建立 2 1 1 服务发现和RFCOMM的连接 2 1 2 支持的特性交换 2 1 3 codec协商 2 1 4 HF指示器 2 1 5 AG指示器 2 1 6
  • ctfshow 文件上传 web151~170

    目录 web151 web 152 web 153 web 154 web 155 web 156 web 157 159 web 160 web 161 web 162 163 web 164 web 165 web 166 web 16
  • STM32F030C8T6 多通道ADC采集

    void adc init void ADC InitTypeDef ADC InitStructure GPIO InitTypeDef GPIO InitStructure RCC ADCCLKConfig RCC ADCCLK PCL
  • 动态规划算法解决背包问题(Java实现)

    文章收藏的好句子 你在书本上花的任何时间 都会在某一个时刻给你回报 目录 1 动态规划算法的概述 2 背包问题 3 动态规划算法解决背包问题 3 1 不可重复装入商品 3 2 思路分析 1 动态规划算法的概述 1 动态规划算法的思想是 将大