1. 程式人生 > >01揹包整理 - 1

01揹包整理 - 1

01揹包的基本問題描述:

  有N件物品和一個容量為V的揹包. 第i件物品的費用是c[i], 價值是w[i]. 求解將哪些物品裝入揹包可使這些物品的費用總和不超過揹包容量, 且價值總和最大.

我們的思路:

  這是最基礎的揹包問題, 特點是: 每種物品僅有一件, 可以選擇放或不放 定義狀態: f[i][v]表示前i件物品恰放入一個容量為v的揹包可以獲得的最大價值. 轉移方程:dp[i][j] = max (dp[i - 1][j], dp[i - 1][j - c[i]] + w[i]) 這個方程非常重要, 基本上所有跟揹包相關的問題的方程都是由它衍生出來的.

  “將前i件物品放入容量為v的揹包中”這個子問題, 若只考慮第i件物品的策略(放或不放), 那麼就可以轉化為一個只牽扯前i-1件物品的問題. 如果不放第i件物品, 那麼問題就轉化為“前i-1件物品放入容量為v的揹包中

”, 價值為f[i-1][v];如果放第i件物品, 那麼問題就轉化為“前i-1件物品放入剩下的容量為v-c[i]的揹包中”, 此時能獲得的最大價值就是f[i-1][v-c[i]]再加上通過放入第i件物品獲得的價值w[i].

 

for (int i = 0; i < n; i ++)
  for (int j = 0; j <= v; j ++)
      if (c[i] < j)    //c: 佔用位置,  w: 錢數
          dp[i][j] = max (dp[i - 1][j],  dp[i - 1][j - c[i]] + w[i]);

  

  我們可以發現: dp[i - 1][j - c[i]] + w[i]中的j - c[i]表示把i這個物體'還回去', 而 + w[i]表示把這個'還回去'的物體的價值加上.

 

優化時間複雜度:
  
以上方法的時間和空間複雜度均為O(N*V), 其中時間複雜度基本已經不能再優化了, 但空間複雜度卻可以優化到O(V). f[2][V] 能否變成f[V]? f[i][v]是由f[i-1][v]和f[i-1][v-c[i]]兩個子問題遞推而來, 能否保證在推f[i][v]時(也即在第i次主迴圈中推f[v]時)能夠得到f[i-1][v]和f[i-1][v-c[i]]的值呢?

 

for (int i = 0; i < n; i ++)
    for (int j = v; j > 0; j --)
        if (c[i] < j)    //c: 佔用位置,  w: 錢數
            dp[j] = max (dp[j],  dp[j - c[i]] + w[i]);

 

 

  其中的dp[j] = max (dp[j], dp[j - c[i]] + w[i])一句恰就相當於我們的轉移方程dp[i][j] = max (dp[i - 1][j], dp[i - 1][j - c[i]] + w[i]), 因為當我們求現在的dp[j - c[i]]就相當於原來的dp[i - 1][v - c[i]], 因為在我們求dp[j]時, dp[v - c[i]]不會改變.

  同理, 我們也可以發現: dp[i - 1][j - c[i]] + w[i]中的j - c[i]表示把i這個物體'還回去', 而 + w[i]表示把這個'還回去'的物體的價值加上.