PS:本文系转载文章,浏览原文可读性会更好,文章开端有原文链接

目录

1、动静布局算法的概述

2、背包问题

3、动静布局算法解决背包问题

 3、1 不可反复装入商品 3、2 思路剖析

1、动静布局算法的概述

(1)动静布局算法的思维是:将大问题分为小问题进行解决,使得一步步取得最优解的解决算法。

(2)动静标准算法与分治算法很相似,思维都是以待解决问题先分解成 n 个子问题,先求解子问题,而后从子问题中失去原问题的解。

(3)动静布局求解的问题与分治算法不同的点在于,通过合成进去的子问题不是互相独立的,下一个子问题的求解是建设在上一个子问题解决的求解根底上的,顺次递进获取最终解。

(4)动静布局能够通过填表的形式一步步推动,最终失去最优解。

2、背包问题

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

图片

3、动静布局算法解决背包问题

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

3、1 不可反复装入商品

我先画一个表格2,而后会对这个表格2进行具体的阐明,如下所示;

图片

阐明:列示意背包能装多大的分量,横示意物品的类型和商品数量,行和列穿插而呈现的数据表示背包能装的物品总和的最大价值。

好,咱们当初对表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)再令 vi 示意在前 i 个商品中可能装入容量为 j 的背包中的最大价值;这句话又是什么意思啊?我再举个例子,看表2的第三行第五列的3000,这时候 i 就是2,j 就是4,vi 就是 v2,也就是 v2 为3000;第二行只能装的商品是鞋子对不对?第三行能装入的商品蕴含第二行装入的商品,也就是说第三行能抉择装入的商品是鞋子和音响;如果鞋子和音响同时放入背包(背包容量为4磅,j=4)必定是装不下的对不对?所以从鞋子和音响外面选最值钱的放入背包中,所以就是 vi 示意在前 i 个商品中可能装入容量为 j 的背包中的最大价值。

当初咱们从思路剖析和表2中可能总结出以下几条公式;

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

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

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

}

程序运行后果如下所示;

图片