动态规划---背包问题分析
0/1背包
問題描述
有N件物品和一個容量為V的背包,第i件物品的體積為c[i],價值為w[i]。求將哪些物品放進背包可以使物品價值總和最大(有兩種情況:不要求填滿背包和填滿背包)。
每件商品只有一件,且只能選擇放或者不放入背包。
解決方案
使用動態規劃求解,定義一個遞歸式opt[i][v]表示前i個物品,在背包容量大小為v的情況下,最大的價值。
opt[i][v] = max(opt[i-1][v], opt[i-1][v-c[i]] + w[i])
其中opt[i-1][v]表示第i件物品不裝入背包中的總價值,而opt[i-1][v-c[i]]+w[i]表示第i件物品裝入背包中的總價值。
通過初始化不同來區別兩種情況,第一種情況,不要求填滿背包,則:
for i <-0 to Vf[i] <- 0第二種情況,要求填滿背包,則
f[0] <- 0 for i <-1 to Vf[i] <- -65536? ? 可以理解為,在要求填滿背包的情況下,我們只選擇上一步的最大價值不小于0的情況,也就是說,在上一步已經滿足了填滿當時容量的條件,表示從0到V的容量里都填滿了物品;而不像不要求填滿背包那樣,只關心最大價值,而不保證每個容量單位里面都有物品。
? ? 由于每個物品只有1件,所以采用從V向下遞減,以此保證每個物品在每次循環過程中只被計算了1遍。
花費的時間復雜度為O(V*T)。
偽代碼
PACKAGE(w, c, V) #ifdef CANEMPTYfor i <- 0 to Vf[i] <- 0 #elsef[0] <- 0for i <- 1 to Vf[i] <- -65536 #endiffor i <- 0 to nfor v <- V downto c[i]f[v] = max(f[v-c[i]] + w[i], f[v])return f[V]代碼
#include <iostream.h>using namespace std; const int V = 100; const int T = 8; int f[V+1]; int w[T] = {3, 4, 6, 2, 3, 5, 1, 4}; int c[T] = {15, 25, 20, 10, 30, 20, 5, 15};int package() { #ifdef EMPTYf[0] = 0;for(int i = 1; i <= V; i++)f[i] = -65536; #elsefor(int i = 0; i <= V; i++)f[i] = 0; #endif for(int i = 0; i < T; i++){for(int v = V; v > c[i]; v--){f[v] = f[v - c[i]] + w[i] > f[v] ? f[v - c[i]] + w[i]: f[v];}}return f[V]; } int main(int argc, char *argv[]) {cout<<"The Max Value is "<<package()<<endl;return 0; }完全背包問題
問題描述
有N種物品(數量不限)和一個容量為V的背包,第i件物品的體積為c[i],價值為w[i]。求將哪些物品放進背包可以使物品價值總和最大(有兩種情況:不要求填滿背包和填滿背包)。
解決方案
動態規劃法,推導出遞歸公式:
f[j] = max(f[j], f[j – w[i]] + v[i])
其中f[j]表示容量為j時的最大價值,f[j-w[i]]+v[i]表示f[j]中包含了第i個物品。
由于每個物品有n件,所以采用從0向上遞加,以此保證每個物品在每次循環過程中只被計算了1遍。
同樣,兩種情況也只是初始化的值不同,同0-1背包問題。
偽代碼
COMPLETE_PACKAGE(w, c, V, N) #ifdef CANEMPTYfor i <- 0 to Vf[i] <- 0 #elsef[0] <- 0for i <- 1 to Vf[i] <- -65536 #endiffor i <- 0 to Nfor j <- c[i] to V f[j] = max(f[j], f[j - c[i]] + w[i])return f[V]代碼
#include <iostream.h> using namespace std; const int V = 100; const int T = 8; int f[V+1]; int w[T] = { 3, 4, 6, 2, 3, 5, 1, 4}; int c[T] = {15, 25, 20, 10, 30, 20, 5, 15};int complete_package() { #ifdef EMPTYfor(int i = 0; i <= V; i++)f[i] = 0; #elsef[0] = 0;for(int i = 1; i <= V; i++)f[i] = -65536; #endiffor(int i = 0; i < T; i++)for(int v = c[i]; v <= V; v++)f[v] = (f[v - c[i]] + w[i]) > f[v] ? (f[v - c[i]] + w[i]):f[v];return f[V]; } int main(int argc, char *argv[]) {cout<<"The Max Value is "<<complete_package()<<endl;return 0; }多重背包問題
問題描述
有N種物品(不定個數)和一個容量為V的背包,第i件物品的體積為c[i],價值為w[i],數量為n[i]。求將哪些物品放進背包可以使物品價值總和最大(有兩種情況:不要求填滿背包和填滿背包)。
解決方案
可以轉換為0-1背包問題。轉換方式為,將第i件物品合并成若干種物品,n[i] – 2^k + 1 > 0即k < log(n[i] – 1),取k的最大值,以2^0,2^1…2^(k-1),n[i]-2^k+1為系數,每件合并后的物品的體積和價值都乘以相應的系數組成不同的物品。如:
第i件物品有13件,那么k的最大值為3,系數為1,2,4,6,組成的新物品的體積分別為c[i], 2*c[i],4*c[i]和6*c[i],價值為w[i],2*w[i],4*w[i]和6*w[i]。這樣,對所有的物品都進行類似的合并,組成一個新的物品集合,然后利用0-1背包來解決剩下的問題。
??? 組成新物品系數選定的理由:可以看出,新的系數可以保證在任意組合后,都能獲取到0~n[i]個物品的數值,如:n[i]=13,那么我想用其中的10個這樣的物品,可以由合并后的系數4和6組成。
偽代碼
Merge_Goods(w, c, n, N)j <- 0for i <- 0 to Np <- 1while p < n[i] + 1nw[j] <- p * w[i]nc[j] <- p * c[i]p <- p * 2j <- j + 1if p / 2 < n[i]nw[j] <- (n[i] - p / 2) * w[i]nc[j] <- (n[i] - p / 2) * c[i]nN = jreturn nN and nw and ncPACKAGE(nw, nc, V, nN) #ifdef CANEMPTYfor i <- 0 to Vf[i] <- 0 #else f[0] <- 0for i <- 1 to Vf[i] <- -65536 #endiffor i <- 0 to nNfor v <- V downto nc[i]f[v] <- max(f[v-nc[i]] + nw[i], f[v])return f[V]代碼
#include <iostream.h>#define MAXNUM 1000 #define EMPTY int nc[MAXNUM]; int nw[MAXNUM]; int merge_goods(int c[], int w[], int n[], int N) {int j = 0;for(int i = 0 ; i < N; i++){int p = 1;while(p < n[i] + 1){nc[j] = p * c[i];nw[j] = p * w[i];p = p * 2;j++;}if(p/2 < n[i]){nc[j] = (n[i] - p / 2) * c[i];nw[j] = (n[i] - p / 2) * w[i];j++;}}return j; }int multi_package(int c[], int w[], int n[], int V, int N) {int nN = merge_goods(c, w, n, N);int f[V+1]; #ifdef EMPTYfor(int i = 0 ; i <= V; i++)f[i] = 0; #elsef[0] = 0;for(int i = 1; i <= V; i++)f[i] = -65536; #endiffor(int i = 0; i < nN; i++){for(int j = V; j >= nc[i]; j--){f[j] = (f[j - nc[i]] + nw[i] > f[j]) ? (f[j - nc[i]] + nw[i]) : f[j];}}return f[V]; }int main(int argc, char *argv[]) {int T = 8;int V = 300;int w[] = { 3, 4, 6, 2, 3, 5, 1, 4};int c[] = {15, 25, 20, 10, 30, 20, 5, 15}; int n[] = { 7, 13, 8, 4, 15, 12, 9, 11};cout<<"Max Value is "<<multi_package(c, w, n, V, T)<<endl;return 0; }二維背包問題
? ? 問題描述
一維、二維或者多維是針對于代價來說的,以前討論的三種背包問題都是在代價為一維的條件下,獲取最大價值的,該問題的代價是二維的,即可以理解為背包有體積和承重兩種代價限制,分別為V和U,獲取在這兩種代價的上限內所能得到的最大價值。
? ? 解決方案
如果理解了之前的背包問題,該問題的解決辦法也就一目了然了,簡單的說,就是將之前的f[v]變為f[v][u],將以前的一次循環,變為了兩次循環,其它沒有理論上的不同了。
如果是0-1背包問題,u和v倒序循環;如果是完全背包問題,則u和v正序進行循環。
f[i][u][v] = max(f[i-1][u][v] , w[i] + f[i-1][u-a[i]][v-b[i]])
? ? 偽代碼(只寫出0-1背包,且不要求某個代價為上限值的情況)
?
Two_Package(w, a, b, U, V, N)for i <- 0 to Ufor j <- 0 to Vf[i][j] <- 0for i <- 0 to Nfor u <- U downto a[i]for v <- V downto b[i]f[v][u] = max(f[u-a[i]][v-b[i]]+w[i], f[u][v])return f[u][v]?
代碼
#include <iostream.h>int two_package(int w[], int a[], int b[], int U, int V, int N) {int f[U+1][V+1];for(int i = 0; i <= U; i++)for(int j = 0; j <= V; j++)f[i][j] = 0;for(int i = 0; i < N; i++){for(int u = U; u >= a[i]; u--){for(int v = V; v >= b[i]; v--)f[u][v] = (f[u - a[i]][v - b[i]] + w[i] > f[u][v]) ? (f[u - a[i]][v - b[i]] + w[i]):f[u][v];}}return f[U][V]; }int main(int argc, char *argv[]) {const int V = 120;const int U = 140;const int T = 8;int w[T] = { 3, 4, 6, 2, 3, 5, 1, 4};int a[T] = {15, 25, 20, 10, 30, 20, 5, 15};int b[T] = {10, 30, 15, 10, 20, 20, 10, 20};cout<<"Max Value is "<<two_package(w, a, b, U, V, T)<<endl;return 0; }分組背包問題
問題描述
有N種物品和一個容量為V的背包,第i件物品的體積為c[i],價值為w[i]。現將所有物品分成若干組,每組中的物品相互沖突,即在選擇時,每組中只能最多選擇一件物品,求將哪些物品放進背包可以使物品價值總和最大(有兩種情況:不要求填滿背包和填滿背包)。
解決方案
可以將k個分組看成k個物品,當做0-1背包問題處理,然后再對每個分組中的單個物品進行選擇。
f[k][v] = max(f[k][v], f[k][v-c[k][i]]+w[i])
偽代碼
Group_Package(w, c, K, V, N)for i <- 0 to Vf[i] <- 0for k <- 0 to Kfor v <- V downto 0for i <- 0 to Nif v > c[k][i]f[v] = max(f[v], f[v - c[k][i]] + w[k][i])return f[V]代碼
#include <iostream.h>const int K = 3; const int N = 4;int w[K][N] = {{3, 4, 6, 2},{3, 5, 1, 4},{6, 2, 3, 5} };int c[K][N] = {{15, 25, 20, 10},{20, 15, 30, 20},{30, 30, 20, 5} }; int group_package(int V) {int f[V + 1];for(int i = 0; i <= V; i++)f[i] = 0;for(int k = 0; k < K; k++){for(int v = V; v >= 0; v--){for(int i = 0; i < N; i++){if(c[k][i] <= v)f[v] = (f[v - c[k][i]] + w[k][i] > f[v]) ? (f[v - c[k][i]] + w[k][i]) : f[v];}}}return f[V]; }int main(int argc, char *argv[]) {int V = 60;cout<<"Max Value is "<<group_package(V)<<endl;return 0; }?
?
?
?
轉載于:https://www.cnblogs.com/geekma/archive/2012/11/28/2793355.html
總結
以上是生活随笔為你收集整理的动态规划---背包问题分析的全部內容,希望文章能夠幫你解決所遇到的問題。
 
                            
                        - 上一篇: iPhone开发笔记[1/50]:初学i
- 下一篇: 读c语言深度剖析 -- 单引号与双引号、
