动态规划——01背包问题(Java实现)

动态规划算法:

动态规划算法介绍:

  1. 动态规划算法的核心思想是:将大问题划分为小问题进行解决,从而一步步获得最优解的处理算法。
  2. 动态规划算法的基本思想是:将待求解的问题分解成若干子问题,先求解子问题,然后从这些子问题的解得到原问题的解。
  3. 适合用于动态规划的求解的问题,经分解得到子问题往往不是互相独立的。(即下一个子阶段的求解是建立在上一个子阶段的解的基础上,进行进一步的求解)
  4. 动态规划可以通过填表的方式来逐步推进,得到最优解。

动态规划算法最佳实践:背包问题

有一个背包,容量为4磅,现有如下物品 :

物品重量价格
吉他(G)11500
音响(S)43000
电脑(L)32000
  1. 要求达到的目标为装入背包的总价值最大,并且重量不超出。
  2. 要求装入的物品不能重复。

思路分析:

  1. 背包问题主要是指一个给定容量的背包、若干具有一定价值和贵重的物品,如何选择物品放入背包使价格最大,其中又分为01背包和完全背包(完全背包指的是:每种物品都有无限件可用)。

  2. 这里的问题属于01背包,即每个物品最多放一个,而无限背包可以转化为01背包。

  3. 算法的主要思想,利用动态规划来解决,每次遍历到的第i个物品,根据 w[i] 和 v[i] 来确定是否需要将物品放入背包。即对于 给定的n个物品,设va[i],w[i] 分别为第i个物品的价值和重量,c为背包的含量。再令v[i][j](二维数组,用于表示下表,行表示价值,列表示容量)表示在前i个物品中能够**装入容量为j **(j 为给顶的背包容量,因为需要不断的测试是否达到最大容量,所有设置容量是一个变量j ) 的背包中的最大价值,则有以下结果。

    两个箭头分别代表v 的 i 和 j (行和列)

在这里插入图片描述

  1. v[i][0] = v[0][j] = 0 表示第一行和第一列为0,如上图。(由于数组下标索引由0开始,这里方便用 i 和 j 代表具体的哪一个)

  2. 当w[i] > j 时 说明背包容量不足以放下第i件物品,只能选择不拿,此时,v[i][j] = v[i-1][j] ;

    即当准备加入新增的商品的重量大于当前背包的 容量时,就使用上一个单元格的装入策略(如上图,音响为4磅,当j = 1,2,3时 装入策略为吉他的价格。

  3. 当 j >= w[i]时(重量有空余 )这是背包容量可以放下第i件物品,可以选择拿还是不拿,判断标准:拿这件物品是否能获取更大的价值。

    v[i][j] = max{v[i-1][j],va[i]+v[i-1][j-w[i]]}

    当准备加入的新增商品的容量小于等于当前背包的容量,判断上一策略跟现有策略哪个大。其中va[i] 表示的是商品的价格。

    其中:

    v[i-1][j] : 就是上一个单元格装入的最大值

    va[i] : 表示当前商品的价值。 j-w[i] 表示剩余的重量

    v[i-1] [j-w[i]]: 装入 i-1商品到剩余空间 j-w[i]的最大值 剩余空间对于其他商品种类的最大价值

    验证公式,如上图 v[3][4] = 2000 + 1500 即i = 3 j = 4

    【j = 4 】 >【 w[3] = 3(磅)】

    v[3][4] = max { v[2][4],va[3]+v[2][1] } = {3000,2000 + 1500}

  4. 如何确定哪些物品构成最大价值?在第三步我们已经可以确定每一种状态可获得的最大价值,但是并不清楚具体选择哪几样物品能够获得最大价值。那么,如何确定哪几样物品能够获得最大价值呢?

    1. 另起一个数组isAdd[N + 1],isAdd[i] = false表示不拿第i个物体,否则表示拿了。

    2. F[i][j],它是最优值,那么如果:

      F[i][j] = F[i - 1][j],说明有没有第i个物品都一样,第i个物品没有放,则isAdd[i] = false;
      否则,说明放了第i个物体,则isAdd[i] = true。

代码分析:

public class KnapsackProblem {
    public static void main(String[] args) {
        int[] w = {1,4,3}; //物品的重量
        int[] val = {1500,3000,2000}; //物品的价值
        int m = 4;//背包的容量
        int n = val.length; //物品的数量

        //创建二维数组:
        // v[i][j] 表示在前i个物品中能够装入容量为j的背包中最大价值
        int[][] v = new int[n+1][m+1];
        //初始化第一行和第一列,本程序可以不处理,因为默认数为0

        // 这里i是从1开始的,下标代表的是二维数组的索引,故一维数组应从0开始,也就是i -1
        for (int i = 1; i < v.length; i++) {
            for (int j = 0; j <v[0].length ; j++) {
                //根据公式,当新增的商品重量大于当前背包的容量时,采取上一次的装入策略
                if (w[i-1] > j){
                    v[i][j] = v[i-1][j];
                }
                //当新增重量等于或者或者小于背包容量时,判断剩余容量的最佳策略 + 当前容量的最佳策略,与上次策略哪个大,取大的。
                else{
                    v[i][j] = Math.max(v[i-1][j],val[i-1] + v[i-1][j - w[i-1]]);
                }
            }
        }
        //确定哪些物品构成最大值
        boolean[] isAdd = new boolean[n+1];
        // n 行的个数
        // w 最大重量
        for (int i = n; i>= 1;i--){
            if (v[i][m] == v[i-1][m])
                isAdd[i] = false;
            else{
                isAdd[i] = true;
                //最大重量-当前重量 
                // 求剩余重量
                m -= w[i-1];
            }
        }
        for(int i = 1;i <= n;i++)
            if(isAdd[i])
                System.out.println(i);
    }
}

//最后输出1 3
  • 18
    点赞
  • 103
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值