01背包问题

01背包问题

01背包问题是一个很经典的动态规划算法题,给你一个固定容量的背包,然后你面前有一堆体积和价值不一的物品,你怎么挑选才能在不超过背包容量的情况下,物品的总价值尽可能高。
你可能一开始的想法是拿每个物品的价值除以体积,计算出每单位空间的价值是多少,然后进行排序,依次选取“性价比”高的物品。但是这种思路不一定能找到最优解,可能选完背包还剩一些空间,调整一下选取的物品可能会获得更大的价值,所以这里就要讲一下动态规划的解法。

思路
比如现在有 n 个物品和一个大小为 m 的背包. 给定数组 A 表示每个物品的大小和数组 V 表示每个物品的价值.
问最多能装入背包的总价值是多大?
m = 10, A = [2, 3, 5, 7], V = [1, 5, 2, 4]

我们思考的就是哪个物品可以选,哪个物品不选,现在用一个二维数组可以把这个过程描述的更加直观。

容量/物品物品0物品1物品2物品3
容量10
容量21
容量31
容量41
容量51
容量61
容量71
容量81
容量91
容量101

当只有物品0时,各个容量的背包能装的物品总价值对多为1。然后当我们有物品1可选时:

容量/物品物品0物品1物品2物品3
容量100
容量211
容量315
容量415
容量516
容量616
容量716
容量816
容量916
容量1016

容量为3 和 4的背包可以选物品1,之后的背包容量够大,两个都要在这里插入图片描述
现在我们再加入物品2供选择

容量/物品物品0物品1物品2物品3
容量1000
容量2111
容量3155
容量4155
容量5166
容量6166
容量7166
容量8167
容量9167
容量10168

在容量为8和9的时候我们把物品0、物品1组合替换为物品1、物品2组合,容量10够大,依旧是全都要!最后加入物品3供选择:

容量/物品物品0物品1物品2物品3
容量10000
容量21111
容量31555
容量41555
容量51666
容量61666
容量71666
容量81677
容量91677
容量101689

最后容量10的时候,我们把物品0、物品1、物品2组合,替换成物品1、物品3组合,得到的最大价值。
那么代码如何实现呢?

public int backPackII(int m, int[] A, int[] V) {
 int[][] dp = new int[m+1][A.length+1];
      for (int i=0;i<A.length ;i++ ){
        for (int u=1;u<=m;u++){
          if(i>0){
            dp[u][i]=u>=A[i] && dp[u-A[i]][i-1]+V[i]> dp[u][i-1]?dp[u-A[i]][i-1]+V[i]:dp[u][i-1];
          }
          else
           dp[u][i]=u>=A[i]?V[i]:0;         
        }
    } 
    return dp[m][A.length-1];
}

其实我们还可以再优化一下,用一个一维数组记录每个容量背包当下的最优解就行。

public int backPackII(int m, int[] A, int[] V) {
   
    int[] dp = new int[m+1];
    for (int i=0;i<A.length ;i++ ){
        for (int u=m;u>=A[i];u--){
           dp[u] = dp[u - A[i]]+V[i]>dp[u]?dp[u - A[i]]+V[i]:dp[u];
        }
    } 
    return dp[m];
}

如果有讲错的地方欢迎大家指正~有疑问的朋友也欢迎交流讨论

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值