0-1背包问题,附上例题(POJ - 3624 Charm Bracelet)
0-1背包问题的题目样式
有 N 件物品和一个容量为 M 的背包。放入第 i 件物品耗费的费用是 Wi,得到的价值是 Vi。求解将哪些物品装入背包可使价值总和最大。
0-1背包问题关键在于该物品放或不放,即在当前容量为M的的情况下,选择不选择该物品,那么就有一个转移方程
for(i=0 - N)
for(j= M - w[i])
dp[i][j] = max(dp[i-1][j],dp[i-1][j-w[i]]+v[i]);
当前物品为i,当前的背包容量为j,如果不选当前该物品,则选取dp[i-1][j]该值。如果选择当前物品,则选取dp[i-1][j+w[i]]+v[i]该值,从两个值当中选取大的值。
最后在dp[N][]中选出最大的,为总和最大的答案
0-1背包的方程优化
上一个转移方程解法,在时间上已基本无法优化,只能在空间上进行优化。从O(NM)-> O(M)
转移方程变为
for(j=M - w[i])
dp[j] = max(dp[j],dp[j-w[i]]+v[i]);
下面解释一下为什么该方程可行:
因为w[i]是个正数,即j+w[i] > j肯定是成立的,j <=j 且j <=j+w[i],所以对于下一次循环j+1肯定不会取到j+1之前的值,所以该转移方程对后续没有影响,是成立的。
最后找出dp[]中最大的就行
给出一道0-1背包的例题
POJ - 3624 http://poj.org/problem?id=3624
Time Limit: 1000MS | Memory Limit: 65536K | |
Total Submissions: 40985 | Accepted: 17828 |
Description
Bessie has gone to the mall's jewelry store and spies a charm bracelet. Of course, she'd like to fill it with the best charms possible from the N (1 ≤ N ≤ 3,402) available charms. Each charm i in the supplied list has a weight Wi (1 ≤ Wi ≤ 400), a 'desirability' factor Di (1 ≤ Di ≤ 100), and can be used at most once. Bessie can only support a charm bracelet whose weight is no more than M (1 ≤ M ≤ 12,880).
Given that weight limit as a constraint and a list of the charms with their weights and desirability rating, deduce the maximum possible sum of ratings.
Input
* Line 1: Two space-separated integers: N and M
* Lines 2..N+1: Line i+1 describes charm i with two space-separated integers: Wi and Di
Output
* Line 1: A single integer that is the greatest sum of charm desirabilities that can be achieved given the weight constraints
Sample Input
4 6 1 4 2 6 3 12 2 7
Sample Output
23
未优化的代码,提交上去是(Memory Limit Exceeded),因为空间复杂度太大
1 #include <iostream> 2 #include <cstring> 3 #include <cstdio> 4 #include <algorithm> 5 using namespace std; 6 int dp[3500][13000]; 7 int main() 8 { 9 int n , m ; 10 int w[3500], v[3500]; 11 12 while(scanf("%d%d",&n,&m) != EOF) { 13 for(int i= 1 ; i <= n ; i++) 14 scanf("%d%d",&w[i],&v[i]); 15 for(int i = 1 ; i <= n; i++) { 16 for(int j = 0; j <= m; j++) { 17 if(j+w[i] <= m) 18 dp[i][j] = max(dp[i-1][j],dp[i-1][j+w[i]]+v[i]); 19 else dp[i][j] = dp[i-1][j]; 20 } 21 } 22 int ans =0; 23 for(int j = 0; j <= m; j++) 24 ans = dp[n][j] > ans ? dp[n][j]:ans; 25 printf("%d\n",ans); 26 } 27 return 0; 28 }
下面给出优化的代码(accpeted)
1 #include <iostream> 2 #include <cstring> 3 #include <cstdio> 4 #include <algorithm> 5 using namespace std; 6 int dp[13000]; 7 int main() 8 { 9 int n , m ; 10 int w[3500], v[3500]; 11 12 while(scanf("%d%d",&n,&m) != EOF) { 13 for(int i= 1 ; i <= n ; i++) 14 scanf("%d%d",&w[i],&v[i]); 15 for(int i = 1 ; i <= n; i++) { 16 for(int j = 0; j <= m; j++) { 17 if(j+w[i] <= m) 18 dp[j] = max(dp[j],dp[j+w[i]]+v[i]); 19 else dp[j] = dp[j]; 20 } 21 } 22 int ans =0; 23 for(int j = 0; j <= m; j++)// 24 ans = dp[j] > ans ? dp[j]:ans; 25 printf("%d\n",ans); 26 } 27 return 0; 28 }