复习:0/1背包和完全背包是如何从二维数组压缩到一维的

  从刚开始接触背包到现在,已经快两个月了,学完背包之后就又继续学其它东西,现在该回头再来复习一下,整理整理自己的思路,使自己对背包更熟练一点,这里自己先复习一下0/1背包和完全背包。

  0/1背包的主要思路就是:这件物品,取还是不取。用一个二维数组dp[i][v]来表示对第i个物品,背包容量为v时的情况。c[i]表示第i件物品的体积,w[i]表示第i件物品的价值。那么考虑第i件物品取与不取:如果不取,那么就可以转化为i-1件物品、容量仍然为v、价值没有增加的情况(dp[i-1][v]);如果取,那么转化为i-1件物品、容量减去第i件物品的体积后剩余容量、价值加上第i件物品的价值后的情况(dp[i-1][ v-c[i] ] + w[i] )。为了让背包中物品价值最大,我们取二者较大者,也就是

dp[i][v]=max{ dp[i-1][v] , dp[i-1][ v-c[i] ] + w[i] }。好了,现在思考如何将数组压缩,对于这两种情况下dp[i][v]值的改变,要么是dp[i][v]=dp[i-1][v],要么是dp[i][v]=dp[i-1][ v-c[i] ] + w[i]。假设下面是就是二维数组dp的一部分,

      a    b    dp[i-1][v]    d    e

      f    g     dp[i][v]       h          k

  我们可以发现:如果dp[i][v]=dp[i-1][v],那么相当于直接复制dp[i][v]上面的元素dp[i-1][v]值。而如果dp[i][v]=dp[i-1][ v-c[i] ] + w[i],注意到,v-c[i]<=v,所以,dp[i][v]的值是由上面红色的元素加上w[i]得到,也就是说,我们每次想要更新dp[i][v],可能会用到的值只有上面红色的部分,所以,我们就能把二维数组压缩为一维数组,只需要每次从后往前更新dp[i][v]的值。这样就用dp[v]来表示容量为v的情况下,背包内物品的价值,状态转移方程也就成了:

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

 

  对于完全背包,一件物品可以取多次,我们仍然使用0/1背包的思想:这件物品,取还是不取。唯一的变化是,取了这件物品,还可以取。所以,如果取,仍然是i件物品的问题( dp[i][ v - c[i]] + w[i]);如果不取,dp[i][v]还是dp[i-1][v]都一样(第一次不取,以后也不会取,相当于转化成i-1件物品的问题,为了和取的情况保持一致,采用dp[i][v])。所以状态转移方程变为了dp[i][v]=max{ dp[i][v] , dp[i][ v-c[i] ] + w[i] },那么在数组里,

      a    b    m        d    e

      f    g     dp[i][v]       h          k

  同样注意到,v-c[i]<=v,所以,每次更新dp[i][v],可能用到的值是红色部分,所以也可以压缩为一维,这里要注意了,数组中的f、g,是i下的情况,并不是i-1的情况,dp[i][v]的值取决取i下,而不是i-1,所以此时应该从前往后更新dp的值,这样才能保证取第i件物品时,dp[i][v]是由dp[i][v-c[i]]+w[i]推得,所以尽管状态转移方程仍然为dp[v]=max{ dp[v] , dp[ v-c[i] ] + w[i] },v的循环顺序却应该是从小到大

 

  基本上,这就是0/1背包和完全背包从二维转化为一维的思路,以后自己还要经常复习。

  0/1背包的伪代码:

for i=1 to N
    for v=V to 0
        f[v]=max{dp[v],dp[v-c[i]]+w[i]}

  完全背包的伪代码:

for i=1 to N
    for v=0 to V
        f[v]=max{dp[v],dp[v-c[i]]+w[i]}

 

 

posted @ 2012-08-30 10:37  等待电子的砹  阅读(1025)  评论(0编辑  收藏  举报