題目:
共n個物體,第i個重量爲w[i],價值v[i],揹包最多能背不超過W的物體,求最大的價值
分析:
每個物體只有一個,在容量允許時(W>w[i]),則對於每個物體只有取、不取兩種選擇
狀態:dp[i][j]:前i個物體,在容量爲j的時候,最大的價值
狀態轉移:
dp[i][j] = max(dp[i-1][j], dp[i-1][j-w[i]] + v[i]);
二維核心:
for(i = 1; i<=n; i++) { for(j = 0; j<=W; j++) { if(j<w[i]) dp[i][j] = dp[i-1][j]; else dp[i][j] = max(dp[i-1][j], dp[i-1][j-w[i]] + v[i]); } }
二維代碼:
#include <stdio.h> #include <iostream> #include <string.h> #include <string> #include <math.h> #include <algorithm> #include <queue> #include <stack> #include <map> #include <vector> using namespace std; int w[100], v[100]; int dp[100][100]; int main() { freopen("a.txt", "r", stdin); int n, W, i, j; while(~scanf("%d%d", &W, &n)) { memset(dp, 0, sizeof(dp)); for(i = 1; i<=n; i++) { scanf("%d%d", &w[i], &v[i]); } for(i = 1; i<=n; i++) { for(j = 0; j<=W; j++) { if(j<w[i]) dp[i][j] = dp[i-1][j]; else dp[i][j] = max(dp[i-1][j], dp[i-1][j-w[i]] + v[i]); } } printf("%d\n", dp[n][W]); } return 0; }
降維:
減行,第i個物體的更新,只依賴於第i-1個的物體的結果
所以可以用滾動數組,每次只存i和i-1時候的值 (可得:dp[n][W] → dp[2][W] )
刪行,第i個物體在容積爲j狀態的更新,只依賴i-1物體容量裏j-w[i]的狀態的結果
所以,從後面開始向前更新,則求j位置時候,j-w[i]的值依舊爲i-1時候的值(可得:dp[n][W] → dp[W] )
一維核心:
for(i = 1; i<=n; i++) { for(j = W; j>=w[i]; j--) //從後向前,此時dp[j-w[i]]相當於dp[i-1][j-w[i]] { dp[j] = max(dp[j], dp[j-w[i]] + v[i]); } }
一維代碼:
#include <stdio.h> #include <iostream> #include <string.h> #include <string> #include <math.h> #include <algorithm> #include <queue> #include <stack> #include <map> #include <vector> using namespace std; int w[100], v[100]; int dp[100]; int main() { freopen("a.txt", "r", stdin); int n, W, i, j; while(~scanf("%d%d", &W, &n)) { memset(dp, 0, sizeof(dp)); for(i = 1; i<=n; i++) { scanf("%d%d", &w[i], &v[i]); } for(i = 1; i<=n; i++) { for(j = W; j>=w[i]; j--) { dp[j] = max(dp[j], dp[j-w[i]] + v[i]); } } printf("%d\n", dp[W]); } return 0; }
初始化:
1、memset(dp, 0, sizeof(dp))
求不超過容積的W的最大價值
容積有剩餘的狀態依舊有值,爲前一個恰好裝滿最優解的值
2、memset(dp, -0x3f, sizeof(dp)); //負無窮、不可達點(當前值約爲:-1e+10)
求恰好裝滿容積的最大價值(可能無解)
當且僅當恰好裝滿的狀態有值,其他存在空白容積的狀態無法到達
常數級優化:
一維中的內循環下限,由j>=w[i] → j>=max{w[i], W-(∑(i,n)w[i])}
1、下限爲j>=w[i]時候
在所有剩餘容積大於等於w[i]時候,選擇取、不取第i物品
2、下限爲j>=max{w[i], W-(∑(i,n)w[i])}時候
只更新在i+1時候需要用到的狀態,並不把所以可能狀態求出
常數級優化代碼:
#include <stdio.h> #include <iostream> #include <string.h> #include <string> #include <math.h> #include <algorithm> #include <queue> #include <stack> #include <map> #include <vector> using namespace std; int w[100], v[100]; int dp[100]; int main() { freopen("a.txt", "r", stdin); int n, W, i, j; while(~scanf("%d%d", &W, &n)) { memset(dp, 0, sizeof(dp)); for(i = 1; i<=n; i++) { scanf("%d%d", &w[i], &v[i]); } int lower, sum = 0; for(i = 1; i<=n; i++) { if(i!=1) sum += w[i-1]; lower = max(sum, w[i]); for(j = W; j>=lower; j--) { dp[j] = max(dp[j], dp[j-w[i]] + v[i]); } } printf("%d\n", dp[W]); } return 0; }