01背包的空间优化

什么是01背包问题:

假设有n件物品

每件物品的体积为w1, w2……wn

相对应的价值为 v1, v2.……vn。

01背包是在n件物品取出若干件放在空间为total_weight的背包里,使得背包的总价值最大。

如何解决这个问题?

我们得到的状态转移方程 是 c[i][j] = max{c[i-1][j], c[i-1][j-w[i]]+v[i]}

相对应的代码如下:

for (int i = 1; i <= n; i++) {  
  for (int j = 1; j <= total_weight; j++) {  
    if (w[i] > j) {  
      c[i][j] = c[i-1][j];  
    } else {  
        if (c[i-1][j] > v[i]+c[i-1][j-w[i]]) {  
          c[i][j] = c[i-1][j];  
        }  
        else {  
          c[i][j] =  v[i] + c[i-1][j-w[i]];  
        }  
    }  
  }  
}  

如何优化它的空间复杂度?

首先肯定的是时间复杂度没有多大的优化空间,但还是有的哦(详情见背包九讲)。每一次c[i][j]改变的值只与c[i-1][x] {x:1...j}有关c[i-1][x]是前一次i循环保存下来的值,因此,可以将c缩减成一维数组。状态转移方程转换为 c[j] = max(c[j], c[j-w[i]]+v[i]);

并且,我们注意到状态转移方程,每一次推导c[i][j]是通过c[i-1][j-w[i]]来推导的,而不是通过c[i][j-w[i]]因此,j的扫描顺序应该改成从大到小

否则,第i次求c数组,必然先求的c[j-w[i]]的值(即c[i][j-w[i]]),再求c[j](即c[i][j])的值由于j递增,那么状态方程就成为下面这个样子了

c[i][j] = max(c[i-1][j], c[i][j-w[i]]+v[i])显然不符合题意。所以代码如下:

for (int i = 1; i <= n; i++) {  
   for (int j = total_weight; j >= 1; j--) {  
     if (w[i] > j) {  
       c[j] = c[j]; //表示第i次与第i-1次相等,这里因为c[j]本来就保存这上一次的值,所以这里不需变化  
     } else {  
       //说明第i件物品的重量小于背包的重量,所以可以选择第i件物品放还是不放  
         if (c[j] > v[i]+c[j-w[i]]) {  
           c[j] = c[j];  
         }  
         else {  
           c[j] =  v[i] + c[j-w[i]];  
         }  
     }  
   }  
 }  
优化下代码后如下:

for (int i = 1; i <= n; i++) {  
  for (int j = total_weight; j >= w[i]; j--) {  
    if (c[j] <= v[i] + c[j-w[i]])  
      c[j] = v[i] + c[j-w[i]];  
  }  
}  

到此,01背包的优化问题已经解决,但请注意,空间优化版本最后是得不出最优解序列的,但起码还是能够求出最大价值,(所以还请慎用)。


伟大的梦想成就伟大的人,从细节做好,从点点滴滴做好,从认真做好。


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值