揹包問題——「徹底揹包」詳解及實現(包含揹包具體物品的求解) ios
徹底揹包是在N種物品中選取若干件(同一種物品可屢次選取)放在空間爲V的揹包裏,每種物品的體積爲C1,C2,…,Cn,與之相對應的價值爲W1,W2,…,Wn.求解怎麼裝物品可以使揹包裏物品總價值最大。 算法
動態規劃(DP): 數組
1) 子問題定義:F[i][j]表示前i種物品中選取若干件物品放入剩餘空間爲j的揹包中所能獲得的最大價值。 測試
2) 根據第i種物品放多少件進行決策 優化
(2-1) spa
其中F[i-1][j-K*C[i]]+K*W[i]表示前i-1種物品中選取若干件物品放入剩餘空間爲j-K*C[i]的揹包中所能獲得的最大價值加上k件第i種物品; .net
設物品種數爲N,揹包容量爲V,第i種物品體積爲C[i],第i種物品價值爲W[i]。 blog
與01揹包相同,徹底揹包也須要求出NV個狀態F[i][j]。可是徹底揹包求F[i][j]時須要對k分別取0,…,j/C[i]求最大F[i][j]值,耗時爲j/C[i]。那麼總的時間複雜度爲O(NV∑(j/C[i])) 排序
由此寫出僞代碼以下: 索引
以上僞代碼數組均爲基於1索引,即第一件物品索引爲1。空間複雜度O(VN)、時間複雜度爲O(NV∑(j/C[i]))
簡單優化:
若兩件物品知足C[i] ≤C[j]&&W[i] ≥W[j]時將第j種物品直接篩選掉。由於第i種物品比第j種物品物美價廉,用i替換j獲得至少不會更差的方案。
這個篩選過程以下:先找出體積大於揹包的物品直接篩掉一部分(也可能一種都篩不掉)複雜度O(N)。利用計數排序思想對剩下的物品體積進行排序,同時篩選出同體積且價值最大的物品留下,其他的都篩掉(這也可能一件都篩不掉)複雜度O(V)。整個過程時間複雜度爲O(N+V)
轉化爲01揹包:
由於同種物品能夠屢次選取,那麼第i種物品最多能夠選取V/C[i]件價值不變的物品,而後就轉化爲01揹包問題。整個過程的時間複雜度並未減小。若是把第i種物品拆成體積爲C[i]×2k價值W[i]×2k的物品,其中知足C[i]×2k≤V。那麼在求狀態F[i][j]時複雜度就變爲O(log2(V/C[i]))。整個時間複雜度就變爲O(NVlog2(V/C[i]))
時間複雜度優化爲O(NV)
將原始算法的DP思想轉變一下。
設F[i][j]表示出在前i種物品中選取若干件物品放入容量爲j的揹包所得的最大價值。那麼對於第i種物品的出現,咱們對第i種物品放不放入揹包進行決策。若是不放那麼F[i][j]=F[i-1][j];若是肯定放,揹包中應該出現至少一件第i種物品,因此F[i][j]種至少應該出現一件第i種物品,即F[i][j]=F[i][j-C[i]]+W[i]。爲何會是F[i][j-C[i]]+W[i]?由於F[i][j-C[i]]裏面可能有第i種物品,也可能沒有第i種物品。咱們要確保F[i][j]至少有一件第i件物品,因此要預留C[i]的空間來存放一件第i種物品。
狀態方程爲:
(2-2)
僞代碼爲:
具體揹包中放入那些物品的求法和01揹包狀況差很少,從F[N][V]逆着走向F[0][0],設i=N,j=V,若是F[i][j]==F[i][j-C[i]]+W[i]說明包裏面有第i件物品,同時j -= C[i]。徹底揹包問題在處理i自減和01揹包不一樣,01揹包是無論F[i][j]與F[i-1][j-C[i]]+W[i]相不相等i都要減1,由於01揹包的第i件物品要麼放要麼不放,無論放仍是不放其已經遍歷過了,須要繼續往下遍歷而徹底揹包只有當F[i][j]與F[i-1][j]相等時i才自減1。由於F[i][j]=F[i-1][j]說明揹包裏面不會含有i,也就是說對於前i種物品容量爲j的揹包所有都放入前i-1種物品才能實現價值最大化,或者直白的理解爲前i種物品中第i種物品物不美價不廉,直接被篩選掉。
打印揹包內物品的僞代碼以下:
和01揹包同樣,也能夠利用一個二維數組Path[][]來標記揹包中的物品。開始時Path[N][V]初始化爲0,當 F[i][j]==F[i][j-C[i]]+W[i]時Path[i][j]置1。最後經過從Path[N+1][V+1]逆着走向Path[0][0]來獲取揹包內物品。其中Path[0][]與Path[][0]爲邊界。一樣,在打印路徑的時候當Path[][]=1時,打印W[i];Path[][]=0時i自減1.
加入路徑信息的僞代碼以下:
打印揹包內物品的僞代碼以下:
優化空間複雜度爲O(V)
和01揹包問題同樣,徹底揹包也能夠用一維數組來保存數據。算法樣式和01揹包的很類似,惟一不一樣的是對V遍歷時變爲正序,而01揹包爲逆序。01揹包中逆序是由於F[i][]只和F[i-1][]有關,且第i件的物品加入不會對F[i-1][]狀態形成影響。而徹底揹包則考慮的是第i種物品的出現的問題,第i種物品一旦出現它勢必應該對第i種物品還沒出現的各狀態形成影響。也就是說,原來沒有第i種物品的狀況下可能有一個最優解,如今第i種物品出現了,而它的加入有可能獲得更優解,因此以前的狀態須要進行改變,故須要正序。
狀態方程爲:
(2-3)
僞代碼以下:
具體揹包中放入那些物品的求法和上面空間複雜度爲O(NV)算法同樣,用一個Path[][]記錄揹包信息。但這裏面是當F[i]=F[i-C[i]]+W[i]時將Path置1.
僞代碼以下:
打印路徑的僞代碼和前面未壓縮空間複雜度時的僞代碼同樣,這裏再也不重寫。
舉例:表2-1爲一個揹包問題數據表,設揹包容量爲10根據上述解決方法可獲得對應的F[i][j]如表2-2所示,最大價值即爲F[6][10].
表2-1揹包問題數據表
物品號i | 1 | 2 | 3 | 4 | 5 | 6 |
體積C | 3 | 2 | 5 | 1 | 6 | 4 |
價值W | 6 | 5 | 10 | 2 | 16 | 8 |
表2-2前i件物品選若干件放入空間爲j的揹包中獲得的最大價值表
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | |
0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
1 | 0 | 0 | 0 | 6 | 6 | 6 | 12 | 12 | 12 | 18 | 18 |
2 | 0 | 0 | 5 | 6 | 10 | 11 | 15 | 16 | 20 | 21 | 25 |
3 | 0 | 0 | 5 | 6 | 10 | 11 | 15 | 16 | 20 | 21 | 25 |
4 | 0 | 2 | 5 | 7 | 10 | 12 | 15 | 17 | 20 | 22 | 25 |
5 | 0 | 2 | 5 | 7 | 10 | 12 | 16 | 18 | 21 | 23 | 26 |
6 | 0 | 2 | 5 | 7 | 10 | 12 | 16 | 18 | 21 | 23 | 26 |
下面針對前面提到的表2-1提供兩種方法的測試代碼:
//時間複雜度O(VN),空間複雜度爲O(VN)
//時間複雜度O(VN),不考慮路徑空間複雜度爲O(V),考慮路徑空間複雜度爲O(VN)
測試代碼: