01 背包问题初识

177 阅读4分钟

前情提要

  之前写了动态规划的相关文章,仅仅只是介绍了动态规划的相关概念,并没有细致深入的去分析动态规划的其他例子,作为动态规划最经典的便是01 背包问题,记得曾经听过这样一句话,所以有关动态规划的问题都可以转换成为01背包问题。

什么是01背包

  有n个物品,它们有各自的体积和价值,现有给定容量的背包,如何让背包里装入的物品具有最大的价值总和?

  为方便讲解和理解,下面讲述的例子均先用具体的数字代入,即:eg:number=4,capacity=8

思路详情

(1)把背包问题抽象化(x1,x2,x3...xn),其中xi取0或者1可以表示选或者不选,vi表示第i个物品的价值,wi表示第i个物品的体积(或重量)

(2)建立模型:max(v1x1+v2x2+....+vnxn)

(3)约束条件为(w1x1+w2x2+....wnxn) < capacity (capacity为包的体积)

(4)定义(vi,j): 当前背包容量为j,前i个最佳组合对应的价值

(5)最优性原理是动态规划重的基础,不论初始状态,初始决策如何,对于前面决策所造成的某一状态而言,其后的各阶段决策必须形成最优

   假设 OPT(x1,x2,...,xn)
   肯定存在OPT(x2,x3...,xn)

   再假设:(Y1,Y2,...Y3)是上述子问题的最优解,则应该有的是
   (v2x2+v1x1+....vnxn)+v1x1 < (v2Y2+v3Y3+....vnYn)+v1x

(6)寻找递推关系式

 ① 包的容量比该商品小,装不下,此时的价值与前i-1个价值一样
   V(i,j) = v(i-1,j)

 ② 有足够容量为装该商品,但装了也不一定能够达到最优解,在装与不装中选择最优的那个
   v(i,j) = max{v(i-1,j);v(i-1;j-w(i)+v(i))} {装不了;装了}
   下面我们列一个相关的表格  i为前i个最佳组合,j为当前背包容量,表格中意味着,当前的最大价值

i/j 0 1 2 3 4 5 6 7 8
0 0 0 0 0 0 0 0 0 0
1 0 0 0 3 3 3 3 3 3
2 0 0 3 4 4 7 7 7 7
3 0 0 3 4 5 7 8 8 9
4 0 0 3 4 5 7 8 9 10

    /**
     * 
     * @param capacity 背包容量
     * @param number 有几个物品
     * @param weight 物品重量数组
     * @param dp dp数组
     * @param value 物品价值数组
     */
    void findMax(int capacity, int number, int[] weight, int[][] dp, int[] value) {
        int i, j;//j为背包剩余容量,i为第i个物品

        //第0行我们统一填的0
        for (i = 1; i < number; i++) {
            for (j = 1; j <= capacity; j++) {
                //如果包装不进的话
                if (j < weight[i]) dp[i][j] = dp[i - 1][j];
                else {
                    //能装的话
                    dp[i][j] = Math.max(dp[i - 1][j], (dp[i - 1][j - weight[i]]) + value[i]);
                }
            }
        }
    }

(7)仅仅把表格填完,最优解就是dp[4][8] = 10 ,需要回溯找出解的组成

 ① dp[i][j] == dp[i-1][j] 时,证明没有选择该i个商品,这个时候去到dp[i-1][j]
 ② dp[i][j] == dp[i-1][j-weight[i]]+value[i]时,说明装了第i个商品,该商品为最优解的一部分,所以我们需要回溯到包装前dp[i-1][j-weight[i]]
 ③ 一直遍历到i=0结束为止,所有解的组成都能够找到

   /**
     * 
     * @param i 前i个商品
     * @param j 体积
     * @param dp 上一步得到的动态规划数组
     */
    void findWhat(int i , int j,int[][] dp ){
            if (i>=0){
                if (dp[i][j]==dp[i-1][j])//没变
                {
                    item[i]=0;//全局变量标记未选中
                    findWhat(i-1,j,dp);
                }else if (j-weight[i]>0&&dp[i][j]==dp[i-1][j-weight[i]]+value[i]){
                    item[i]=1;//标记已经被选中
                    findWhat(i-1,j-weight[i],dp);
                }
            }
    }

总结

这是动态规划一类比较经典的问题,很多都可以转换成为背包问题来解决,但背包问题也有很多变种,之前看过一篇pdf背包九讲,讲的很详细。希望能给你带来帮助哈

ps:最近太忙惹 都没时间更新博客 都调时间更新的呜呜呜呜呜 太难惹