有$N$件物品和一個容量爲$V$的揹包。第$i$件物品的體積是$C_i$,其價值是$W_i$。求解,在不超過揹包容量狀況下,將哪些物品裝入揹包可以使價值總和最大。php
這是最基礎的揹包問題,特色是:每種物品僅有一件。
狀態 $F[i,v]$表示前$i$件物品中選擇若干件放在容量爲$v$的揹包中,能夠取得的最大價值。
轉移方程
$$
F[i,v]=\max {F[i−1,v],F[i−1,v−C_i]+W_i}
$$
對於第$i$件物品,有放與不放兩種選擇。若選擇不放,$F[i,v]=F[i−1,v]$;若選擇放,$v−C_i$確保有足夠的空間,隨之$F[i,v]=F[i−1,v−C_i]+W_i$。ios
/** * * author 劉毅(Limer) * date 2017-03-17 * mode C++ */ #include<iostream> #include<algorithm> using namespace std; int main() { const int N = 6; //物品個數 const int V = 10; //揹包體積 int C[N + 1] = { -1,5,6,5,1,19,7 }; //第i個物品的體積(下標從1開始) int W[N + 1] = { -1,2,3,1,4,6,5 }; //第i個物品的價值 int F[N + 1][V + 1] = { 0 }; //狀態 for (int i = 1; i <= N; i++) //對於第i個物品 for (int v = 0; v <= V; v++) { F[i][v] = F[i - 1][v]; //第i個不放 if (v - C[i] >= 0 && F[i][v] < F[i - 1][v - C[i]] + W[i]) //若是比它大,再放第i個 F[i][v] = F[i - 1][v - C[i]] + W[i]; } cout << "最大價值是:" << F[N][V] << endl; //9 return 0; }
以上方法的時間和空間複雜度均爲$O(VN)$,其中時間複雜度應該已經不能再優化了,但空間複雜度卻能夠優化到$O(V)$。
先考慮上面講的基本思路如何實現,確定是有一個主循環i ← 1 to N
,每次算出來二維數組$F[i,v]$的全部值。那麼,若是隻用一個數組$F[v]$能不能保證第$i$次循環結束後$F[v]$中表示的就是咱們定義的狀態$F[i,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]$的值呢?
事實上,這要求在每次主循環中咱們以v ← V to C[i]
的遞減順序計算$F[v]$,這樣才能保證計算$F[v]$時$F[v−C_i]$保存的是狀態$F[i−1,v−C_i]$的值。
優化後的代碼以下:c++
/** * * author 劉毅(Limer) * date 2017-03-17 * mode C++ */ #include<iostream> #include<algorithm> using namespace std; int main() { const int N = 6; //物品個數 const int V = 10; //揹包體積 int C[N + 1] = { -1,5,6,5,1,19,7 }; //第i個物品的體積(下標從1開始) int W[N + 1] = { -1,2,3,1,4,6,5 }; //第i個物品的價值 int F[V + 1] = { 0 }; //狀態 for (int i = 1; i <= N; i++) //對於第i個物品 for (int v = V; v >= C[i]; v--) F[v] = max(F[v], F[v - C[i]] + W[i]); cout << "最大價值是:" << F[V] << endl; //9 return 0; }
咱們看到的求最優解的揹包問題題目中,事實上有兩種不太相同的問法。有的題目要求「剛好裝滿揹包」時的最優解,有的題目則並無要求必須把揹包裝滿。這兩種問法的實現方法只是在初始化的時候有所不一樣。
若是是第一種問法,要求剛好裝滿揹包,那麼在初始化時除了F[0]爲0,其它F[1]...F[V]均設爲−∞,這樣就能夠保證最終獲得的F[V]是一種剛好裝滿揹包的最優解。若是並無要求必須把揹包裝滿,而是隻但願價格儘可能大,初始化時應該將F[0]...F[V]所有設爲0。
這是爲何呢?能夠這樣理解:初始化的F數組事實上就是在沒有任何物品能夠放入揹包時的合法狀態。若是要求揹包剛好裝滿,那麼此時只有容量爲0的揹包能夠在什麼也不裝且價值爲0的狀況下被「剛好裝滿」,其它容量的揹包均沒有合法的解,屬於未定義的狀態,應該被賦值爲-∞了。若是揹包並不是必須被裝滿,那麼任何容量的揹包都有一個合法解「什麼都不裝」,這個解的價值爲0,因此初始時狀態的值也就所有爲0了。數組
參考文獻:
[ 1 ] .揹包九講.優化
文章轉自個人我的博客:http://www.61mon.com/index.php/archives/188/spa