动态规划之0/1背包问题

来源:互联网 发布:百度读书软件 编辑:程序博客网 时间:2024/05/20 09:06

网上介绍动态规划和背包问题的文章多不胜数,这里就不再去粘贴复制哪些概念性的东西了,直接切入主题。

先将0/1背包问题的分析简单概括一下,我从正反两个方向阐述:

场景:现在有若干个物品,每个物品有自己的重量和价值,背包有一定的容量,在背包能容纳的下的情况下随便装入物品(物品不可分割),要求获取的价值要最大化。(本文请忽略背包用"容量"来装"重量"~~,还有为了通俗,也尽量少用数学符号)

正向分析如果前i个物品在背包容量为j时,我们获取到了最优解,达到了最大价值(定义此最大价值为V),如果再多一个物品(给这个物品一个标识:x,重量为wx),背包容量不变仍为j,问题变成了,前i+1个物品在背包容量为j时的最大价值,那么为多少呢?

对于x物品来说,它的命运只有两个:放/不放。

我们首先需要考虑一个最基本的问题,这个容量j能否放的下x物品?不然说再多都是浪费表情,如果放不下,那么很简单,我们直接不理它,此时即使多了x物品,最大价值也=V。

如果放得下,我们再来考虑放还是不放(我们要价值最大化,所以取两者间的最大值)

1>如果不放,那么很简单,我们直接不理他,此时得到一个价值V1(显然V1==V)

2>如果放,那我们就需要重新分配这个背包,此时的最大价值为:x的价值(要放x,肯定得算上它的价值噻) + 前i个物品(这些物品我们没有动,该咋的咋的)在容量为 j-wx(放入x后,背包的可分配容量只剩j-wx)时的最大价值,我们定义为V2。所以,此时最大价值=V1 > V2 ? V1 : V2

反向分析上面的正向分析,直接假设了前i个物品在背包容量为j时的最优解V,那么我们来考虑V=啥?

我们认为i物品的重量为wi,其实和上面的分析一样,V=第i个物品的价值 + 前i-1个物品在容量为j-wi时的最大价值,等等,这个等式成立的条件是什么?

没错,前提是我们为了获取最优解,是将i物品放入背包了的,而i物品放入背包的条件又是什么?当然是容量能够容纳i,并且在能够容纳i的情况下,放i比不放i,最终的价值更大。

下面列出代码(java,随便写的测试代码,看个意思就行了,仅供参考~~)

正向:嵌套for循环方式,此方式为从最小子问题(0个物品,背包容量为0)开始到最终解(i个物品,背包容量为j)。使用二维数组保存子问题最优解

反向:递归

package com.loren;/** * Created by loren on 2017/12/16. */public class Test01BeiBao {    //物品数量    static int num = 4;    //每个物品的价值数组    static int value[] = {10, 40, 30, 50};    //每个物品的重量数组和value一一对应    static int weight[] = {5, 4, 6, 3};    //背包容量    static int W = 10;    public static void main(String[] args) {        System.out.println("for循环-结果:" + getResult(num, value, weight, W));        System.out.println("递归-结果:" + getResultDG(num - 1, W));    }    /**     * 获取结果(递归版)     *     * @param i 物品下标从0开始,表示前i个物品     * @param j     * @return     */    public static int getResultDG(int i, int j) {        if (j == 0) {            //背包容量为0时,最大价值=0            return 0;        }        if (i == 0) {            //递归到前0个物品时,表示背包容量为j,只有第一个物体可选时的最大价值            //如果j容量能够容纳下该物品,返回该物品的价值,否则返回0            if (j >= weight[0]) {                return value[0];            } else {                return 0;            }        }        //前i个物品在容量为j时的最大价值=i-1个物品在容量为j-weight[i]的最大价值+value[i]        //这个结论的前提是为了达到最大价值,是将第i个物品放入背包了的,否则最大价值=i-1个物品在容量为j时的价值        //而第i个物品是否放入背包的判断条件是:背包容量允许放入此物品且放入后的价值比不放的价值高        //判断是否需要放入此物品        if (weight[i] <= j) {            int a = getResultDG(i - 1, j - weight[i]) + value[i];            int b = getResultDG(i - 1, j);            return a > b ? a : b;        } else {            return getResultDG(i - 1, j);        }    }    /**     * @param value     * @param weight     * @param W      背包重量     * @return     */    public static int getResult(int num, int[] value, int[] weight, int W) {        //初始化二维数组,用于保存i个物品在容量为j是的最大价值        int[][] tab = new int[num + 1][W + 1];//整个二维数组需要包含0个物品在容量为j时和i个物品在容量为0时的最大价值,需要        //初始化数组,0个物品在容量为j时和i个物品在容量为0时的最大价值均为0        //之所以初始化tab[0][i]和tab[i][0],是为了在计算到第一个物品时方便,便于理解        for (int i = 0; i <= W; i++) {            tab[0][i] = 0;        }        for (int i = 0; i <= num; i++) {            tab[i][0] = 0;        }        for (int i = 1; i <= num; i++) {            //外层循环每个物品            for (int j = 1; j <= W; j++) {                //内层循环当前背包容量                if (weight[i - 1] <= j) {                    //如果当前物品能够放进背包,有两种情况:放或者不放,当前最大价值只需要取两种情况之间的最大值即可                    //放:最大价值=当前物品的价值+(i-1)个物品在(当前容量-当前容量)时的最大价值                    int v1 = value[i - 1] + tab[i - 1][j - weight[i - 1]];                    //不放:最大价值=(i-1)个物品在当前容量时的最大价值                    int v2 = tab[i - 1][j];                    //取最大值                    tab[i][j] = v1 >= v2 ? v1 : v2;                } else {                    //如果当前物品不能放进背包,那么价值就为i-1个物品(除去当前物品)在j容量时的价值                    tab[i][j] = tab[i - 1][j];                }            }        }        //输出整个二维数组        System.out.print("                           ");        for (int i = 0; i <= W; i++) {            System.out.print("容量:" + i + "       ");        }        System.out.println();        for (int i = 0; i < tab.length; i++) {            System.out.print("物品:" + i + "value=" + (i > 0 ? value[i - 1] : 0) + ",weight=" + (i > 0 ? weight[i - 1] : 0) + (i == 0 ? "       " : "      "));            for (int j = 0; j < tab[0].length; j++) {                System.out.print(tab[i][j] + (tab[i][j] >= 10 ? "           " : "            "));            }            System.out.println();        }        System.out.println("----------------------");        return tab[value.length][W];//返回所有物品在容量为W时的最大价值    }}

原创粉丝点击