基本算法0/1背包问题

  关于0/1背包问题网上有非常多的博文,在此我谨记录一下自己的理解。

  问题表述:有N件物品和一个容量为V的背包。第i件物品的体积是C[i](0<=i<=N-1),价值是W[i]。求解将哪些物品装入背包可使价值总和最大。每个物品最多只可以放入背包一次。

  这个问题的经典解法思路如下:

  我们用f[i][j]表示在考虑前i个物品时体积为j的背包的最大价值,注意,我们并不是把前i个物品全部放入背包,而是考虑i个物品中挑选一些放入背包,使得价值最大的那些情况。

  首先,我们考虑只有1个物品(第0个)时,容量分别为0,1,...,V的各背包所包含物品的最大价值。很明显,容量大于等于C[0]的背包的最大价值为W[0],容量小于C[0]的背包的最大价值为0。

  然后,我们考虑再来一个(第i个)物品时,容量分别为0,1,...,V的各背包所包含物品的最大价值。对于某个背包,我们有两种选择:将该物品放入该背包或者不放入。

  当我们将该物品放入某个体积为j的背包时,该背包的最大价值为f[i][j-C[i]]+W[i]。当我们不把该物品放入某个体积为j的背包时,该背包的最大价值为f[i-1][j]。所以在考虑前i个物品时,体积为j的背包的最大价值为f[i][j]=max{f[i-1][j],f[i][j-C[i]]+W[i]}.

  迭代以上步骤,从i=0到N-1,最后得到的f[N-1][V]就是最后的答案。

  上述算法的时间复杂度为O(VN),空间复杂度也是O(VN)。时间复杂度是最优的,而空间复杂度可以进一步优化:我们注意到在公式f[i][j]=max{f[i-1][j],f[i][j-C[i]]+W[i]}中,对于j从0到V,f[i][j]只与f[i-1][j]有关,而与i-2,i-3等情况下的f无关,所以我们只需要考虑前一次迭代(亦即i-1)的结果就可以。亦即f[j]=max{f[j],f[j-C[i]]+W[i]}.又因为在计算f[j]时用到了比j小的f:f[j-C[i]],所以在对j进行迭代时应该从后向前迭代: 

1   for(int i=0;i<N;i++){
2             for(int j=V;j>=0;j--){
3                 if(j-item[i][0]>=0){//此处判断是为了防止将j物品放入容量小于C[j]的背包中
4                     f[j]=max(f[j],f[j-item[i][0]]+item[i][1]);
5                 }
6             }           
7         }

  我们可以用一个例子来展示一下上述代码迭代的过程。取V=10,N=3.三个物品的体积分别为3,4,5.价值分别为4,5,6,迭代过程中f数组的值为:

  0 0 0 4 4 4 4 4 4 4 4
  0 0 0 4 5 5 5 9 9 9 9
  0 0 0 4 5 6 6 9 10 11 11

  第一行为只考虑第1个物品的情况。所有容量大于等于3的背包的价值都为该物品的价值:4.

  第二行为只考虑前2个物品的情况。所有容量大于等于7的背包可以同时容纳前2个物品,价值为4+5=9,容量为4-6的背包可以容纳第2个物品,价值为5,容量为3的背包可以容纳第1个物品,价值为4.

  第三行以此类推。

  我们取最后1行的最后一个数字为结果,亦即考虑所有3个物品的体积为10的背包的最大价值,为11.

参考文献:

  [1] 0/1问题 动态规划法

  [2] 背包问题九讲 第一讲 0/1背包问题

  [3] 背包之0/1背包 完全背包 多重背包详解

 

原文地址:https://www.cnblogs.com/kemaswill/p/2711665.html