1. 程式人生 > >【演算法】基礎揹包

【演算法】基礎揹包

基礎揹包

題目

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

基本思路

這是最基礎的揹包問題,特點是:每種物品僅有一件,可以選擇放或不放。 用子問題定義狀態:即f[i][v]表示前i件物品恰放入一個容量為v的揹包可以獲得的最大價值。則其狀態轉移方程便是: f[i][v]=max{ f[i-1][v], f[i-1][v-w[i]]+v[i] }。 可以壓縮空間,f[v]=max{f[v],f[v-w[i]]+v[i]} 這個方程非常重要,基本上所有跟揹包相關的問題的方程都是由它衍生出來的。所以有必要將它詳細解釋一下:“將前i件物品放入容量為v的揹包中”這個子問題,若只考慮第i件物品的策略(放或不放),那麼就可以轉化為一個只牽扯前i-1件物品的問題。如果不放第i件物品,那麼問題就轉化為“前i-1件物品放入容量為v的揹包中”,價值為f[i-1][v];如果放第i件物品,那麼問題就轉化為“前i-1件物品放入剩下的容量為v-w[i]的揹包中”,此時能獲得的最大價值就是f [i-1][v-w[i]]再加上通過放入第i件物品獲得的價值v[i]。 注意f[v]有意義當且僅當存在一個前i件物品的子集,其費用總和為f[v]。所以按照這個方程遞推完畢後,最終的答案並不一定是f[N] [V],而是f[N][0..V]的最大值。如果將狀態的定義中的“恰”字去掉,在轉移方程中就要再加入一項f[v-1],這樣就可以保證f[N] [V]就是最後的答案。至於為什麼這樣就可以,由你自己來體會了。  

空間複雜

以上方法的時間和空間複雜度均為O(N*V),其中時間複雜度基本已經不能再優化了,但空間複雜度卻可以優化到O(V)。 先考慮上面講的基本思路如何實現,肯定是有一個主迴圈i=1..N,每次算出來二維陣列f[i][0..V]的所有值。那麼,如果只用一個數組f [0..V],能不能保證第i次迴圈結束後f[v]中表示的就是我們定義的狀態f[i][v]呢? f[i][v]是由f[i-1][v]和f [i-1][v-w[i]]兩個子問題遞推而來,能否保證在推f[v]時(也即在第i次主迴圈中推f[v]時)能夠得到f[v]和f[v -w[i]]的值呢?事實上,這要求在每次主迴圈中我們以v=V..0的順序推f[v],這樣才能保證推f[v]時f[v-w[i]]儲存的是狀態f[i-1][v-c[i]]的值。虛擬碼如下: for i=1..N for v=V..0 f[v]=max{f[v],f[v-w[i]]+v[i]}; 其中的f[v]=max{f[v],f[v-w[i]]}一句恰就相當於我們的轉移方程f[i][v]=max{f[i-1][v],f[i-1][v-w[i]]},因為的 f[v-w[i]]就相當於原來的f[i-1][v-w[i]]。如果將v的迴圈順序從上面的逆序改成順序的話,那麼則成了f[i][v]由f[i][v-w[i]]推知,與本題意不符,但它卻是另一個重要的揹包問題P02最簡捷的解決方案,故學習只用一維陣列解01揹包問題是十分必要的。