zoukankan      html  css  js  c++  java
  • 01 背包问题

    01 背包问题

    问题描述

    给定 n 件物品,物品的重量为 weight[i],物品的价值为 value[i]。现挑选物品放入背包中,假定背包能承受的最大重量为 W,问应该如何选择装入背包中的物品,使得装入背包中物品的总价值最大?

    每个动态规划都从一个网格开始


    动态规划——二维空间

    令dp[i] [k] 表示前i件物品放入容量为k的背包中所获取的最大价值

    初始条件:i=0或者k=0,dp[i] [k] 均为0

    状态转移方程,对于编号为i的物品:

    • 如果选择放入背包,那么当前背包的最大价值为当前第i件物品的价值减去第i件物品重量后剩余空间所能容纳的最大价值之和,即dp[i-1] [k-weight[i]] + value[i]

    • 如果选择不放入背包,那么当前背包的总价值为dp[i-1] [k]

    • 所以dp[i] [k]应选择两者的较大值:

      Max(dp[i-1] [k],dp[i-1] [k-weight[i]]  + value[i]) ; k>=weight[i]
      

    代码实现:

    public static int backpack(int[] weights, int[] values, int W) {
          if (weights == null || weights.length == 0) return 0;
    
          int n = weights.length;
          int[][] dp = new int[n + 1][W + 1];
          for (int i = 1; i <= n; i++) {
              for (int k = 1; k <= W; k++) {
                  // 存放第i件物品
                  dp[i][k] = k >= weights[i - 1] ? dp[i - 1][k - weights[i - 1]] + values[i - 1] : 0;
                  // max(存放第i件物品,不存放第i件物品)所能获取的最大价值
                  dp[i][k] = Math.max(dp[i][k], dp[i - 1][k]);
              }
          }
          return dp[n][W];
      }
    

    动态规划+压缩空间

    从上面代码实现中可看出来,dp[i] [...] 只和dp[i-1] [...]有关,所以没必要使用O(nW)的辅助空间,仅用O(W)的辅助空间

    状态转移方程变成dp[k] (新值) = Max( dp[k-weight[i]] + values[i] , dp[k] (旧值))

    代码实现

    public static int backpack(int[] weights, int[] values, int W) {
        if (weights == null || weights.length == 0) return 0;
    
        int n = weights.length;
        int[] dp = new int[W + 1];
        for (int i = 0; i < n; i++) {
            // 索引较小的元素可能会被覆盖,此处需要后往前
            for (int k = W; k >= 1; k--) {
                // 存放第i件物品
                int valueWith_i = k >= weights[i] ? dp[k - weights[i]] + values[i] : 0;
                // 不存放第i件物品
                int valueWithout_i = dp[k];
                // max(存放第i件物品,不存放第i件物品)所能获取的最大价值
                dp[k] = Math.max(valueWith_i, valueWithout_i);
            }
        }
        return dp[W];
    }
    

    k < weights[i]时dp[k]=max(dp[k],0),相当于没有更新,所以可以进行简化,更早结束循环

    public static int backpack(int[] weights, int[] values, int W) {
        if (weights == null || weights.length == 0) return 0;
    
        int n = weights.length;
        int[] dp = new int[W + 1];
        for (int i = 0; i < n; i++) {
            // 索引较小的元素可能会被覆盖,此处需要后往前
            for (int k = W; k >= weights[i] ; k--) {
                // 存放第i件物品
                int valueWith_i =dp[k - weights[i]] + values[i];
                // 不存放第i件物品
                int valueWithout_i = dp[k];
                // max(存放第i件物品,不存放第i件物品)所能获取的最大价值
                dp[k] = Math.max(valueWith_i, valueWithout_i);
            }
        }
        return dp[W];
    }
    


    完全背包问题

    问题描述

    有N种物品和一个容量为T的背包,每种物品都就可以选择任意多个,第i种物品的价值为Value[i],体积为weight[i],求解:选哪些物品放入背包,可使得这些物品的价值最大,并且体积总和不超过背包容量。

    和01 背包类似,完全背包也是一个经典的DP问题,两者不同之处在于完全背包是有n种物品,不同物品可以取任意多次,而在01背包问题中每种物品至多只能取1次

    令dp[i] [k] 表示前i件物品放入容量为k的背包中所获取的最大价值

    初始条件:i=0或者k=0,dp[i] [k] 均为0

    状态转移方程,对于编号为i的物品:

    • 如果选择放入背包,此时和01 背包不同,01背包选择对第i件物品是否放入背包,而完全背包每种物品任意多个,此时的背包最大价值是dp[i] [k-w[i]] + V[]

    • 如果选择不放入背包,情况和 01 背包相同,当前背包的最大价值为dp[i-1] [k]

    • 此时dp[i] [k]选择两者较大值

      Max(dp[i-1] [k],dp[i] [k-weight[i]] + Value[i]) ;k>=weight[i]
      

      代码实现

      public static int backpack(int[] weights, int[] values, int W) {
              if (weights == null || weights.length == 0) return 0;
      
              int n = weights.length;
              int[][] dp = new int[n + 1][W + 1];
              for (int i = 1; i <= n; i++) {
                  for (int k = 1; k <= W; k++) {
                      // 存放第i件物品
                     int valueWith_i = k >= weights[i - 1] ? dp[i ][k - weights[i]] + values[i - 1] : 0;
                      // 不存放第i件物品
                      int valueWithout_i= dp[i - 1][k]
                      // max(存放第i件物品,不存放第i件物品)所能获取的最大价值
                      dp[i][k] = Math.max(valueWith_i, valueWithout_i);
                  }
              }
              return dp[n][W];
          }
      

    同样利用动态规划和压缩空间

    状态转移方程变成dp[k] (新值) = dp[j] (旧值) + dp[j-values[i]];

    public static int backpack(int[] weights, int[] values, int W) {
            if (weights == null || weights.length == 0) return 0;
    
            int n = weights.length;
            int[] dp = new int[n + 1][W + 1];
            for (int i = 0; i < n; i++) {
                for (int k = 1; k <= W; k++) {
                  if (k - values[i] >= 0)
                  dp[k] = dp[k] + dp[k-values[i]];
                }
            }
            return dp[W];
        }
    


    空间压缩
    • 第i行的新值 依赖于 第i行当前位置的旧值及前面一个的新值

    • 第i行的新值 依赖于 第i-1行的旧值、第i行前面的新值

    • 第i行的新值 依赖于 第i行当前位置及前面所有位置的旧值

  • 相关阅读:
    理解区块链之前,先上手体验一把数字货币(2018-04-06 陈浩 第6讲)
    约瑟夫·卢宾《以太坊:从底层揭秘区块链应用和机会》2018-04-21
    以太坊智能合约介绍,Solidity介绍
    新浪微博 [异常问题] 414 Request-URL Too Large
    Google自动广告,将广告代码放置在 HTML 中的什么位置?
    囤币一族,被中国市场遗忘的价值币ADA
    基于EOS开发的Dapp大全
    朴素贝叶斯算法,贝叶斯分类算法,贝叶斯定理原理
    区块链3.0 ada Cardano卡尔达诺如何获得一致好评?
    拜占庭将军问题(Byzantine Generals Problem),一个关于分布式系统容错问题故事
  • 原文地址:https://www.cnblogs.com/fyusac/p/14872109.html
Copyright © 2011-2022 走看看