菜鳥都能理解的0-1揹包問題的空間優化

如果你不知道什麼叫做0-1揹包問題,下面是0-1揹包問題的簡單描述

假設有n件物品

每件物品的體積爲w1, w2……wn

   相對應的價值爲 v1, v2.……vn。

01揹包是在n件物品取出若干件放在空間爲total_weight的揹包裏,使得揹包的總體積最大

關於0-1揹包問題沒有優化版本,請看這裏

上面的核心代碼是下面這一段

  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]];


如此優美的代碼簡直無法想象!

注意,空間優化版本最後是求解不出來最優解序列的,但是能求出最優解,也就是最大價值


發佈了140 篇原創文章 · 獲贊 70 · 訪問量 17萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章