0-1背包问题,以及空间优化

什么是0-1背包问题?

有一个容量为 N 的背包,要用这个背包装下物品的价值最大,这些物品有两个属性:体积 w 和价值 v。

定义一个二维数组 dp 存储最大价值,其中 dp[ i ][ j ] 表示前 i 件物品体积不超过 j 的情况下能达到的最大价值。设第 i 件物品体积为 w,价值为 v,根据第 i 件物品是否添加到背包中,可以分两种情况讨论:

  • 第 i 件物品没添加到背包,总体积不超过 j 的前 i 件物品的最大价值就是总体积不超过 j 的前 i - 1 件物品的最大价值,dp[ i ][ j ] = dp[ i - 1 ][ j ]。
  • 第 i 件物品添加到背包中,dp[ i ][ j ] = dp[ i - 1 ][ j - w ] + v。

第 i 件物品可添加也可以不添加,取决于哪种情况下最大价值更大。因此,0-1 背包的状态转移方程为:
在这里插入图片描述

// W 为背包总体积
// N 为物品数量
// weights 数组存储 N 个物品的重量
// values 数组存储 N 个物品的价值
public int knapsack(int W, int N, int[] weights, int[] values) {
    int[][] dp = new int[N + 1][W + 1];
    for (int i = 1; i <= N; i++) {
        int w = weights[i - 1], v = values[i - 1];
        for (int j = 1; j <= W; j++) {
            if (j >= w) {
                dp[i][j] = Math.max(dp[i - 1][j], dp[i - 1][j - w] + v);
            } else {
                dp[i][j] = dp[i - 1][j];
            }
        }
    }
    return dp[N][W];
}
2.空间优化

将二维数组降为一维数组,一维数组就必须要逆序遍历。因为:f[i][v]与f[i - 1][v]以及f[i - 1][v - c[i]]有关的。当我们逆序遍历的时候,我们数组前一部分,是上一轮i - 1遍历的结果,刚好符合要求;而如果我们顺序遍历的话,我们就会把i - 1轮的数据给覆盖掉。即:数组前面的f[0] f[1] … f[v - 1] 都已经改变过,里面存的都不是 i - 1时刻的值。因此,必须采用逆序遍历的方式。

3.举一个例子
设有3件物品 ,背包能容纳的总重量为10
i=1,2,3

物品号         重量(c)          价值(w)
i=1             4                 5

i=2             7                 9

i=3             5                 6
f[v]=max{ f[v],f[v-c[i]]+w[i] }
如果v是顺序递增 i=1时,v=4~10 (因为v要至少大于等于c[i]嘛 不然减出个负数没意义)
原先的:  f[0]=0 f[1]=0 f[2]=0 f[3]=0 f[4]=0 f[5]=0 f[6]=0 f[7]=0 
f[8]=0 f[9]=0  f[10]=0
---------------------------  i=1  -------------------------------- 
后来的: f[0]=0 f[1]=0 f[2]=0 f[3]=0 f[4]=5 f[5]=5 f[6]=5 f[7]=5 
f[8]=0 f[9]=0  f[10]=0

v=4:
f[4]=max{f[4],f[0]+5}    max{0,5}=5   f[4]=5

v=5:
f[5]=max{f[5],f[1]+5}    max{0,5}=5   f[5]=5

v=6:
f[6]=max{f[6],f[2]+5}    max{0,5}=5   f[6]=5

v=7:
f[7]=max{f[7],f[3]+5}    max{0,5}=5   f[7]=5  (这里就有问题,容量
为7的时候,最大价值应该为9)

v=8:
f[8]=max{f[8],f[4]+5}    max{0,10}=10  f[8]=10  (这里显然不对,这时i=1,只
能放一件物品,然而没有一个物品的价值为10)

v=9:
f[9]=max{f[9],f[5]+5}    max(0,10}=10  f[9]=10

v=10:
f[10]=max{f[10],f[6]+5}  max{0,10}=10  f[10]=10
既然顺序 i=1都不对 i=23就不用看了 由此看来顺序不行
=====================================================================
=====================================================================
下面再来看看逆序的 我为了方便看 把上面的数据复制过来
设有3件物品 ,背包能容纳的总重量为10
i=1,2,3

物品号         重量(c)          价值(w)
i=1             4                 5

i=2             7                 9

i=3             5                 6

f[v]=max{ f[v],f[v-c[i]]+w[i] }

如果v是逆序,v=10~4               
---------------------------  i=1  --------------------------------
f[0]=0 f[1]=0 f[2]=0 f[3]=0 f[4]=5 f[5]=5 f[6]=5  f[7]=5   f[8]=5 
f[9]=5  f[10]=5

v=10:
max{f[10],f[6]+5}     max{0,5}=5      f[10]=5

v=9:
max{f[9],f[5]+5}      max{0,5}=5      f[9]=5

v=8:
max{f[8],f[4]+5}      max{0,5}=5      f[8]=5

v=7:
max{f[7],f[3]+5}      max={0,5}=5     f[7]=5

v=6:
max{f[6],f[2]+5}      max={0,5}=5     f[6]=5

v=5:
max{f[5],f[1]+5}      max={0,5}=5     f[5]=5

v=4:
max{f[4],f[0]+5}      max={0,5}=5     f[4]=5

---------------------------  i=2  --------------------------------
f[0]=0 f[1]=0 f[2]=0 f[3]=0 f[4]=5 f[5]=5 f[6]=5  f[7]=9   f[8]=9
 f[9]=9  f[10]=9

v=10:
max{f[10],f[3]+9}      max{5,9}=9   f[10]=9

v=9:
max{f[9],f[2]+9}      max{5,9}=9    f[9]=9

v=8:
max{f[8],f[1]+9}      max{5,9}=9    f[8]=9

v=7:
max{f[7],f[0]+9}      max{5,9}=9    f[7]=9

---------------------------  i=3  --------------------------------
f[0]=0 f[1]=0 f[2]=0 f[3]=0 f[4]=5 f[5]=5 f[6]=5  f[7]=9   f[8]=9 
f[9]=9  f[10]=9

v=10:
max{f[10],f[5]+6}     max{9,11}=11   f[10]=11

v=9:
max{f[9],f[4]+6}      max{9,11}=11   f[9]=11

v=8:
max{f[8],f[3]+6}      max{9,6}=9     f[8]=9

v=7:
max{f[7],f[2]+6}      max{9,6}=9     f[7]=9

v=6:
max{f[6],f[1]+6}      max{5,6}=6     f[6]=6

v=5:
max{f[5],f[0]+6}      max{5,6}=6     f[5]=6

总结:只有逆序的时候,i - 1轮遍历的数据才不会被覆盖。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值