背包问题-动态规划算法

动态规划算法

背包问题,有一个背包,容量为4kg,现在有如下物品:
吉他,重量1kg,价格1500;
音响,重量4kg,价格3000;
电脑,种类3kg,价格2000;
如何将装入背包的物品的总价值最大,并且重量不超出背包的容量,装入的物品不能重复

背包问题的思路分析
背包问题指的是一个给定容量的背包、若干具有一定价值和重量的物品,如何选择物品放入背包使物品的价值最大。背包问题又分01背包和完全背包
01背包指的是每种物品最多放一个,完全背包指的是每种物品都有无限件可用,而无限背包可以转化为01背包

用一个二维数组来表示背包,二维数组的行表示背包的容量,二维数组的列表示物品的种类
在这里插入图片描述

  1. 对于给定的n个物品,每次遍历到第i个物品,设value[i]为第i个物品的价值,w[i]为第i个物品的重量;C为背包的容量。设v[i][j]表示在前i个物品中能够装入容量为j的背包中的最大价值

  2. 二维数组v的行代表背包的容量,二维数组v的列代表物品的种类,v[i][0]=0,表示当背包的容量为0时,背包中物品的价值为0;v[0][j]=0,表示当背包中的物品为0时,背包中物品的价值也为0

  3. 遍历二维数组v的时候,会先确定v[1][1]的值,然后紧接着遍历v[1][i],并执行4,5步的判断与赋值;然后会遍历v[2][i],并执行4,5步的判断与赋值,以此类推

  4. 当w[i]>j时,表示当准备新增的商品的容量大于当前背包的容量时,就有v[i][j]= v[i-1][j],即使用当前背包容量下的此新增商品添加之前的装入策略

  5. 当j>= w[i]时,表示当准备新增的商品的容量小于等于当前背包的容量时,有两种装入方式
    a) v[i-1][j],就是当前背包容量下的此新增商品添加之前的装入策略
    b) value[i]+v[i-1][j-w[i]],value[i]就是此新增商品的价值,v[i-1][j-w[i]],v[i-1]表示的就是在此新增商品添加之前,背包容量为[j-w[i]]时,装入商品的最大值

  6. 对这两种装入方式取最大值,v[i][j]=max{v[i-1][j], v[i]+v[i-1][j-w[i]]},即为当前条件下背包问题的最优解,当二维数组v遍历完后,v中的最大值即为背包问题的最优解

代码实现动态规划算法-背包问题的解决

package dynamicProgramming;

public class KnapsackProblem {
    public static void main(String[] args) {
        int[] w = {1, 4, 3};//物品的重量
        int[] value = {1500, 3000, 2000};//物品的价值
        int m = 4;//背包的容量
        int n = value.length;//物品的个数
      
      /*
       * 创建二维数组,行数为物品的个数+1,列数为背包的容量+1
       * v[i][j]表示在前i个物品中能够装入容量为j的背包中的最大价值
       */
        int[][] v = new int[n + 1][m + 1];
        //记录放入商品的情况,使用二维数组来记录
        int[][] path = new int[n + 1][m + 1];

        //初始化第一行和第一列
        for(int i = 0; i < v.length; i++) {
            v[i][0] = 0;//初始化第一列
        }

        for(int i = 0; i < v[0].length; i++) {
            v[0][i] = 0;//初始化第一行
        }

        //动态规划处理
        for(int i = 1; i < v.length; i++) {//不处理第一行,所以i从1开始遍历
            for(int j = 1; j < v[0].length; j++) {//不处理第一列,所以j从1开始遍历
            /*
             * 新增的商品的容量大于当前背包的容量时
             * 此时w[i-1]才表示新增商品的容量,因为i是从1开始遍历的
             */
                if(w[i - 1] > j) {
                    v[i][j] = v[i-1][j];
                }else {
               /*
                * 新增的商品的容量小于等于当前背包的容量时,即w[i - 1] <= j
                * 要注意,因为i是从1开始遍历的,所以数组w和数组value中的参数都为i-1
                */
                    //v[i][j] = Math.max(v[i-1][j], value[i-1]+v[i-1][j-w[i-1]]);
               /*
                * 为了记录商品存放到背包的情况,不能简单使用上面的赋值语句
                */
                    if(v[i-1][j] < value[i-1]+v[i-1][j-w[i-1]]) {

                        v[i][j] = value[i-1]+v[i-1][j-w[i-1]];

                        //把当前的商品情况记录到path
                        path[i][j] = 1;
                    }else {
                        v[i][j] = v[i-1][j];
                    }
                }
            }
        }

        //输出v,即遍历二维数组
        for(int i = 0; i < v.length; i++) {
            for(int j = 0; j < v[0].length; j++) {
                System.out.print(v[i][j] + " ");
            }
            System.out.println();
        }
      
      /*
       * 输出最优解对应的商品
       * 不能直接for循环遍历path来获取path[i][j] = 1的i的情况
       * 因为path[i][j] = 1对应的if条件执行了较多次数,所以path[i][j] = 1有较多条
       * 最优解对应的商品信息,其实就是path的最后一条数据所对应的商品
       * 所以解决思路是逆向遍历path数组
       */

        int i = path.length - 1;//path数组行的最大下标
        int j = path[0].length - 1;//path数组列的最大下标

        // 逆向遍历path数组
        while(i > 0 && j > 0) {
         /*
          * 逆向遍历path[i][j] == 1,只要遍历到第一个满足条件的,那么这个就是问题的最优解
          * 如果第一次while循环,path[i][j] == 1,说明path数组的最后一条数据对应的商品就是问题的最优解
          * 但是假如不是,就跳过if条件,因为不一定最优解就包含了最后一件商品
          * 此时使i--,即从倒数第二件商品进行while循环,并判断path[i][j]是否等于1
          */
            if(path[i][j] == 1) {
            /*
             * 如果满足path[i][j] == 1这个条件,说明此时的i,也是商品下标
             * 就是最优解中的一件商品
             */
                System.out.printf("第%d个商品放入到背包中", i);
                System.out.println();
            /*
             * 此时要将j减去此时i对应的商品容量
             * 因为还要进行while循环,而此时要找的是最优解中的其他商品
             * 此时肯定要从减去了已经寻找到的商品的容量的背包中进行寻找
             * 这其实相当于逆向的进行一次背包问题的求解
             */
                j = j - w[i - 1];
            }
         /*
          * 最后无论if条件是否满足,都要进行i--,即从当前商品的上一件商品进行循环遍历
          * 假如if条件不满足,当前的商品不在最优解中,下一次遍历就可以去掉当前商品
          * 假如if条件满足,当前的商品在最优解中,下一次遍历还是要去掉当前商品,因为这是01背包,每件商品只能放入一次背包
          */
            i--;
        }
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值