原題網址:https://www.lintcode.com/problem/backpack/descriptionhtml
在n個物品中挑選若干物品裝入揹包,最多能裝多滿?假設揹包的大小爲m,每一個物品的大小爲A[i]面試
你不能夠將物品進行切割。segmentfault
若是有4個物品[2, 3, 5, 7]數組
若是揹包的大小爲11,能夠選擇[2, 3, 5]裝入揹包,最多能夠裝滿10的空間。markdown
若是揹包的大小爲12,能夠選擇[2, 3, 7]裝入揹包,最多能夠裝滿12的空間。app
函數須要返回最多能裝滿的空間大小。函數
O(n x m) time and O(m) memory.post
O(n x m) memory is also acceptable if you do not know how to optimize memory.優化
思路:url
揹包問題是動態規劃的一種題型,它的特色以下:
1. 用值做爲dp維度
2. dp過程就是填寫矩陣
3. 能夠用滾動數組進行優化 轉自此文
dp【i】【j】表示前 i 個物品放到容量爲 j 的揹包裏可以佔用的最大致積。
狀態轉移方程爲:dp【i】【j】= max(dp【i-1】【j】,dp【i-1】【j-A【i】】+A【i】)。
每一個物品只有兩種狀態,放或者不放。對於容量爲 j 的揹包,放入第 i-1 件物品後佔用的最大致積爲dp【i-1】【j】,如今考慮第 i 件物品。
不放,dp【i】【j】=dp【i-1】【j】;
放,須要從 j 中騰出A【i】的空間,再看剩餘的空間放前 i-1 件物品最大能佔多少空間,即dp【i】【j】=A【i】+ dp【i-1】【j-A【i】】。(注意前提是 j >= A【i】)
最後的dp【i】【j】就是上述兩種狀況的較大值。
AC代碼,時間複雜度O(m×n),空間複雜度O(m×n):
class Solution { public: /** * @param m: An integer m denotes the size of a backpack * @param A: Given n items with size A[i] * @return: The maximum size */
int backPack(int m, vector<int> &A) { // write your code here
if (A.empty()) { return 0; } int size=A.size(); vector<vector<int>> dp(size,vector<int>(m+1,0)); //初始化第一行,即只有第一個物品時,若揹包容量大於等於A[0],放物品能佔用的最大空間爲A[0];
for (int j=0;j<=m;j++) { if (j>=A[0]) { dp[0][j]=A[0]; } } //計算dp其餘元素;
for (int i=1;i<size;i++) { for (int j=0;j<=m;j++) { if (j>=A[i])//能放A[i],計算此時的最大致積,注意是大於等於;
{ dp[i][j]=dp[i-1][j-A[i]]+A[i]; } dp[i][j]=max(dp[i-1][j],dp[i][j]); } } return dp[size-1][m]; } };
PS:狀態轉移方程簡而言之就是,揹包容量爲 j 時,能放得下 i 就騰出A【i】的空間,再看剩餘的空間放前 i-1 件物品最大能佔多少空間,兩者之和與不放 i 能佔用的最大致積比,哪一個大就取哪一個;放不下就直接看0~i-1能佔用 j 的最大容量是多少;
再PS:dp數組優化前,j是從前向後遍歷仍是從後向前遍歷都不影響其結果,由於計算當前i是參照i-1時的數據。
利用滾動數組優化空間複雜度:
建立一維動態數組dp,dp【j】仍然表示前 i 個物品放到容量爲 j 的揹包裏可以佔用的最大致積。
狀態轉移方程爲:j從m依次遞減到0,dp【j】= max(dp【j】,dp【j-A【i】】+A【i】)。
推導思路與二維dp相同。只不過代碼實現的時候要注意,列方向須要從後向前計算(防止覆蓋未參加計算的上一行數據),這樣就實現了用當前行不斷代替前一行。
不難理解,i=0時是正常計算的。i≠0時,二維dp【i】【j】= max(dp【i-1】【j】,dp【i-1】【j-A【i】】+A【i】),注意等號右邊的dp數組行下標都是i-1,即前一行的數據。一維dp時,j從後向前遍歷,則參與計算的dp【j】與dp【j-A【i】】都是上一行的值。
AC代碼:
class Solution { public: /** * @param m: An integer m denotes the size of a backpack * @param A: Given n items with size A[i] * @return: The maximum size */
int backPack(int m, vector<int> &A) { // write your code here
int size=A.size(); vector<int> dp(m+1,0); for (int i=0;i<size;i++) { for (int j=m;j>=0;j--)//從後向前計算,保證了參與運算的是上一行的dp[j]與dp[j-A[i]];
{ if (j>=A[i])//注意是大於等於;
{ dp[j]=max(dp[j],dp[j-A[i]]+A[i]); } } } return dp[m]; } };
參考:
lintcode backpack 揹包問題 講解清晰易懂
LintCode揹包問題總結 總結了一系列揹包問題,能夠好好參考。