01揹包空間優化

weixin_33749242發表於2017-06-23

參考 http://www.2cto.com/kf/201301/184347.html
條件
1 每件物品的體積為w1, w2……wn
2 相對應的價值為 v1, v2.……vn
3 01揹包是在n件物品取出若干件放在空間為total_weight的揹包裡,使得揹包的總體積最大

01揹包沒優化的版本

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] = max{c[i-1][j], c[i-1][j-w[i]]+v[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]];  
  }  
}  

最後關於j的掃描順序改為從大到小的圖解。

2196908-19ba0b65e2c64cff.png
動態規劃.png

假設現在需要求i件物品時c[10] (即j=10)的值顯然它是需要i-1時的c[10]和c[9]去推匯出並將求得的值賦值給c[10],從圖中不難看出當j的有小到大順序時是有問題的。因為推導c[10]所需的c[9]因為第i-1件物品下的c[9],顯然這種情況下的c[9]並不是第i-1件物品時所對應的c[9]的值與轉移方程矛盾。

相關文章