0-1揹包html
問題描述:數組
有n個重量和價值分別是wi,vi的物品,從這些物品中挑選出總重量不超過W的物品,求全部方案中價值總和的最大值優化
分析:spa
dp[ i ][ j ] 表示在前 i 個物品中能裝入容量爲 j 的揹包的最大價值,則有:code
dp[i][j] = dp[i - 1][j] j < wi dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - wi] + vi) j >= wi
當前揹包容量不容許裝入第 i 件物品時,前 i 件和前 i-1 件同樣,容許裝入時,從兩種選擇(裝、不裝)中挑選價值最大的htm
優化及拓展:blog
在計算 dp[i][j] 時只使用了 dp[i-1][0……j] ,在存儲子問題解的時候,只用存儲 dp[i-1] 的子問題解便可,因此可使用滾動數組進行空間優化,用一維數組代替二維,只不過一維的數組,一個是表明正在解決的問題(左邊的是 i ),一個是表明子問題(右邊的是 i - 1)。即狀態轉移方程能夠這樣表示:dp[j] = max(dp[j], dp[j - w[i]] + v[i]); get
注意點:若使用滾動數組優化成一維,則 j 的遍歷,即揹包容量的枚舉須要逆序枚舉,由於一維數組正序計算時存在值的覆蓋(i-1時刻的值會被當前 i 時刻的值覆蓋),會使得結果錯誤。而二維數組標明瞭是 i 時刻的值仍是 i-1 時刻的值,因此正序逆序都無所謂博客
徹底揹包class
問題描述:
在0-1揹包的基礎上,每種物品能夠取任意個
分析:
徹底揹包問題的dp思想能夠在0-1揹包的基礎上改進,增長一個描述選取物品數量的參數,這樣的話還須要枚舉數量參數的可取值,效率不高
for(int i = 1; i <= n; i++) { for(int j = 1; j <= w; j++) { for(int k = 0; k * w[i] <= j; k++) { if(w[i] <= j) dp[i][j] = max(dp[i][j], dp[i - 1][j - k * w[i]] + k * v[i]); else dp[i][j] = dp[i - 1][j]; } } }
雖然在這基礎上能夠再進行一系列的優化,好比有兩種物品,其中一種價值小且重量大,那麼就能夠把這種物品刪除不考慮等等,可是在隨機數據中,這種優化效果並不顯著,現更換一種dp思路,將徹底揹包問題轉化成0-1揹包來解決。
上面這種k參數的方法是以每一種物品爲單位來枚舉每種取多少個來解決的,而如今以每個物品爲單位來看問題,考慮是否在物品總數中添加當前該物品,來看遞推式
f [ i ] [ j ] = max( f[ i - 1 ] [ j ] , f [ i ] [ j - c[i] ] + w [ i ] )
爲何會是f[i][j-c[i]]+w[i],由於你放第i種物品,並不牽扯到第i-1種物品
1 for (int i = 1; i <= n; ++i) 2 { 3 for (int j = 1; j <= v; ++j) 4 { 5 if (c[i] <= j) 6 f[i][j] = max(f[i - 1][j], f[i][j - c[i]] + v[i]); 7 else 8 f[i][j] = f[i - 1][j]; 9 } 10 }
優化及拓展:
空間優化一下,和0-1揹包問題不一樣的是,0-1揹包空間優化後遍歷順序是逆序,而這裏徹底揹包問題則是須要順序。
考慮一下緣由,在未考慮第 i 種物品的時候,前面的 i - 1 種物品存在一個最優解,當考慮第 i 種物品的添加時,可能最優解會更新,因此須要順序來覆蓋更新最優解
1 for (int i = 1; i <= n; ++i) 2 { 3 for (int j = w[i]; j <= v; ++j) 4 { 5 f[j] = max(f[j], f[j - c[i]] + v[i]); 6 } 7 }
參考博客: