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的揹包中
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]表示把這個'還回去'的物體的價值加上.