本文主要爲了記載用,詳細解釋請本身根據提示親自畫圖操做下,切忌眼高手低。html
1、裝滿揹包ios
裝滿揹包,要求最紅認定最大val值的前提是——此刻揹包恰好裝滿,僅從實現上看,和「可不滿」揹包的區別只是初始化方法不一樣。以0-1揹包爲例,ide
V=10,N=3,c[]={3,4,5}, w={4,5,6}優化
1)揹包不必定裝滿spa
計算順序是:從右往左,自上而下:由於每一個物品只能放一次,前面的體積小的會影響體積大的.net
2)揹包恰好裝滿 3d
計算順序是:從右往左,自上而下。注意初始值,其中-inf表示負無窮code
關於裝滿揹包,讀者能夠本身體會下-inf做用,親自劃一下圖,就明白爲什麼如此初始化了。htm
參考自《經典揹包問題 01揹包+徹底揹包+多重揹包》blog
2、0-1揹包
問題描述:有N件物品和一個容量爲V的揹包。第i建物品的費用是c[i],價值是w[i]。求解將哪些物品裝入揹包可以使價值總和最大
狀態方程1:dp[i][j] = max{dp[i - 1][j], dp[i - 1][j - c[i] + w[i]]}
解決方案1:資料不少,此處從略。
狀態方程2:優化空間,用f[v]代替dp[i][v]:max{f[v], f[v-c[i]] + w[i]}
for i in 0 ... N for v = V ... 0 f[v] = max{f[v], f[v-c[i]] + w[i]}
至於爲何v是從V到0,本身劃一下才能明白。網上說法一大堆,不是本身的理解。
解決方法2:
#include <stdio.h> #include <stdlib.h> #include <string.h> #define N 1010 int value[N], volume[N], dp[N]; // 0-1揹包,優化空間 void dpPackage(int n, int v) { int i, j; memset(dp, 0, sizeof(dp)); for (i = 1; i <= n; i ++) { for (j = v; j >= volume[i]; j --) { dp[j] = dp[j] > dp[j - volume[i]] + value[i] ? dp[j] : dp[j - volume[i]] + value[i]; } } printf("%d\n", dp[v]); } int main(void) { int i, t, n, v; scanf("%d", &t); while (t --) { // 接收參數 scanf("%d %d", &n, &v); for (i = 1; i <= n; i ++) scanf("%d", value + i); for (i = 1; i <= n; i ++) scanf("%d", volume + i); // 0-1揹包 dpPackage(n, v); } return 0; }
解決方法3(遞歸):這是最直接的實現動態規劃的方法,但每每被人們忽略,摘自《0-1揹包問題的遞歸實現與非遞歸實現》,源碼貼在下面
#include<iostream> using namespace std; const int W = 150; const int number = 5; const int VALUE[] = {60, 20, 10, 60, 100}; const int WEIGHT[] = {20, 30, 50, 60, 80}; //function Make( i {處理到第i件物品} , j{剩餘的空間爲j}) :integer; int Make(int i, int j) { int r1 = 0; int r2 = 0; int r = 0; if (i == -1) { return 0; } if(j >= WEIGHT[i]) //揹包剩餘空間能夠放下物品 i { r1 = Make(i-1,j - WEIGHT[i]) + VALUE[i]; //第i件物品放入所能獲得的價值 r2 = Make(i-1,j); //第i件物品不放所能獲得的價值 r = (r1>r2)?r1:r2; } return r; } void main() { int maxValue = Make(number-1, W); cout<<"maxValue: "<<maxValue<<endl; }
3、徹底揹包
問題描述:有N種物品和一個容量爲V的揹包,每種物品都有無限件可用。第i種物品的費用是c[i],價格是w[i].求解將哪些物品裝入揹包可以使這些物品的費用總和不超過揹包容量,且價值總和最大
狀態方程1:dp[i][v] = max{dp[i-1][v - k * c[i]] + k * w[i] | 0 <= k * c[i]<= v}
解決方法1:參考《徹底揹包基本實現》
using namespace std; int maxV[11][201]; int weight[11]; int value[11]; int V, N; void main() { int i, j, k; scanf("%d %d",&V, &N); for(i = 0; i < N; ++i) { scanf("%d %d",&weight[i],&value[i]); } for(i = 0; i < N; ++i) { for(j = 0; j <= V; ++j) { if(i > 0) { maxV[i][j] = maxV[i-1][j]; if(j/weight[i] >= 1) { int max_tmp = 0; for(k = 1; k <= j/weight[i]; ++k) { if(maxV[i-1][j-k*weight[i]] + k*value[i] > max_tmp) { max_tmp = maxV[i-1][j-k*weight[i]] + k*value[i]; } } maxV[i][j] = maxV[i][j] > max_tmp ? maxV[i][j] : max_tmp; } }else { if(j/weight[0] >= 1) { maxV[0][j] = j/weight[0] * value[0]; } } } } printf("%d",maxV[N-1][V]); }
狀態方程2:f[v] = max{f[v], f[v-cost] + weight} 這個方案的獲得,依賴1)轉化爲0-1揹包問題 2)並壓縮空間,參見《01揹包、徹底揹包、多重揹包》
for i = 1 ... N for v = 0 ... V f[v] = max{f[v], f[v-cost] + weight}
解決方法2:
/* * 徹底揹包問題 */ #include <stdio.h> #include <stdlib.h> #define INF 50000000 typedef struct coin { int price, weight; } coin; void dynamicPackage(coin *coins, int n, int v) { if (v < 0) { printf("This is impossible.\n"); return; } int i, j, *dp; // 動態分配內存 dp = (int *)malloc(sizeof(int) * (v + 1)); // 初始化 dp[0] = 0; for (i = 1; i <= v; i ++) dp[i] = INF; // 徹底揹包問題 for (i = 1; i <= n; i ++) { for (j = coins[i].weight; j <= v; j ++) { dp[j] = (dp[j] < dp[j - coins[i].weight] + coins[i].price) ? dp[j] : dp[j - coins[i].weight] + coins[i].price; } } if (dp[v] >= INF) printf("This is impossible.\n"); else printf("The minimum amount of money in the piggy-bank is %d.\n", dp[v]); // 清理內存 free(dp); dp = NULL; } int main(void) { int t, e, f, n, i; coin *coins; scanf("%d", &t); while (t --) { scanf("%d %d", &e, &f); scanf("%d", &n); // 接收貨幣 coins = (coin *)malloc(sizeof(coin) * (n + 1)); if (coins == NULL) exit(-1); for (i = 1; i <= n; i ++) { scanf("%d %d", &coins[i].price, &coins[i].weight); } // 徹底揹包 dynamicPackage(coins, n, f - e); free(coins); coins = NULL; } return 0; }
4、0-1揹包 和 徹底揹包 裝滿方案數
先說下,問題的意義,
1)徹底揹包裝滿
假設如今有1元、2元、5元的紙幣不少張,如今須要20塊錢,你能給多少種找錢方案,這就能夠認爲是徹底揹包問題,即揹包容量爲20,物品體積分別爲一、二、5。
2)01揹包裝滿
給定一個數m,將m拆成不一樣的天然數的和的形式有多少種方案,這就是典型的01揹包問題,揹包容量爲m,物品件數爲k,這裏面的k是隱含條件,能夠求出來,由於m最多由1+2+…+k獲得,由此能夠根據m求得物品件數的上限。此題非遞歸見下文「揹包問題」,遞歸方法見
《解題筆記(31)——從數列1,2...n中隨意取幾個數,使其和等於m》
具體解決方法,這裏再也不陳述,參見《揹包問題——「01揹包」及「徹底揹包」裝滿揹包的方案總數分析及實現》
此題也能夠先求出小於m的全部可能的數的組合方案,而後從中找出方案內數字和等於m的。具體可參考《解題筆記(21)——字符串的排列組合問題》
另外,對於能夠不滿的狀況,有以下博文分析方案總數,
5、多重揹包
待續。
6、動態規劃之《TSP旅行商問題》