代码如下:
#include <iostream> using namespace std; /* 0-1背包 版本1 * Time Complexity O(N*V) * Space Complexity O(N*V) * 设 V <= 200 N <= 10 * 状态转移方程:f(i,v) = max{ f(i-1,v), f(i-1,v-c[i])+w[i] } */ int maxValue[11][201]; /* 前i个物体面对容量j的最大价值,即子问题最优解 */ int weight[11]; int value[11]; int V, N; void main() { int i, j; 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) /* 容量为V 等号 */ { if(i > 0) { maxValue[i][j] = maxValue[i-1][j]; if(j >= weight[i]) /* 等号 */ { int tmp = maxValue[i-1][j-weight[i]] + value[i]; maxValue[i][j] = ( tmp > maxValue[i][j]) ? tmp : maxValue[i][j]; } }else /* 数组第0行赋值 */ { if(j >= weight[0]) maxValue[0][j] = value[0]; } } } printf("%d",maxValue[N-1][V]); /* 重定向输出结果到文件 */ freopen("C:\\dp.txt","w",stdout); for(i = 0; i <= N; ++i) { for(j = 0; j <= V; ++j) { printf("%d ",maxValue[i][j]); } printf("\n"); } }测试用例:
程序输出的状态转移矩阵如下图,第一行表示第1个物体对于容量0至V时的最优解,即背包最大价值。该实现方法是0-1背包最基本的思想,追踪状态转移矩阵有助于加深理解,POJ上单纯的0-1背包题目也有不少,如3624等,可以水一下,加深理解。
========================================
#include <iostream> using namespace std; /* 0-1背包 版本2 * Time Complexity O(N*V) * Space Complexity O(2*V) * 设 V <= 200 N <= 10 * 状态转移方程:f(i,v) = max{ f(i-1,v), f(i-1,v-c[i])+w[i] } */ int maxValue[2][201]; /* 前i个物体面对容量j的最大价值,即子问题最优解 */ 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) /* 容量为V 等号 */ { if(i > 0) { k = i & 1; /* i%2 获得滚动数组当前索引 k */ maxValue[k][j] = maxValue[k^1][j]; if(j >= weight[i]) /* 等号 */ { int tmp = maxValue[k^1][j-weight[i]] + value[i]; maxValue[k][j] = ( tmp > maxValue[k][j]) ? tmp : maxValue[k][j]; } }else /* 数组第0行赋值 */ { if(j >= weight[0]) maxValue[0][j] = value[0]; } } } printf("%d",maxValue[k][V]); /* 重定向输出结果到文件 */ freopen("C:\\dp.txt","w",stdout); for(i = 0; i <= 1; ++i) { for(j = 0; j <= V; ++j) { printf("%d ",maxValue[i][j]); } printf("\n"); } }这种空间循环滚动使用的思想很有意思,类似的,大家熟悉的斐波那契数列,f(n) = f(n-1) + f(n-2),如果要求解f(1000),是不需要申请1000个大小的数组的,使用滚动数组只需申请3个空间f[3]就可以完成任务。
#include <iostream> using namespace std; /* 0-1背包 版本3 * Time Complexity O(N*V) * Space Complexity O(V) * 设 V <= 200 N <= 10 * 状态转移方程:v = V...0; f(v) = max{ f(v), f(v-c[i])+w[i] } */ int maxV[201]; /* 记录前i个物品中容量v时的最大价值 */ int weight[11]; int value[11]; int V, N; void main() { int i, j; scanf("%d %d",&V, &N); for(i = 0; i < N; ++i) { scanf("%d %d",&weight[i],&value[i]); } /* * 对于第i轮循环 * 求出了前i个物品中面对容量为v的最大价值 */ for(i = 0; i < N; ++i) { /* * 内循环实际上讲maxV[0...v]滚动覆盖前一轮的maxV[0...V] * 可输出对照使用二维数组时的情况 * j从V至0逆序是防止有的物品放入背包多次 */ for(j = V; j >= weight[i]; --j) /* weight > j 的物品不会影响状态f[0,weight[i-1]] */ { int tmp = maxV[j-weight[i]]+value[i]; maxV[j] = (maxV[j] > tmp) ? maxV[j] : tmp; } } printf("%d",maxV[V]); }可以看出,使用一维数组,代码非常简练。
#include <iostream> using namespace std; int maxV[201]; /* 记录前i个物品中容量v时的最大价值 */ int weight[11]; int value[11]; int V, N; void main() { int i, j; scanf("%d %d",&V, &N); for(i = 0; i < N; ++i) { scanf("%d %d",&weight[i],&value[i]); } for(i = 1; i <= V; ++i) /* 初始化非法状态 */ { maxV[i] = -100; } for(i = 0; i < N; ++i) { for(j = V; j >= weight[i]; --j) { int tmp = maxV[j-weight[i]]+value[i]; maxV[j] = (maxV[j] > tmp) ? maxV[j] : tmp; } } }
为了加深理解,输出每轮循环的状态矩阵如下,对照每个物体的情况,就会理解为什么做那样的初始化了。
========================================
#include <iostream> using namespace std; /* 0-1背包 输出最优方案 2 直接根据状态数组算 * Time Complexity O(N*V) * Space Complexity O(N*V) * 设 V <= 200 N <= 10 * 状态转移方程:f(i,v) = max{ f(i-1,v), f(i-1,v-c[i])+w[i] } */ int maxValue[11][201]; /* 记录子问题最优解 */ int weight[11]; int value[11]; int V, N; void main() { int i, j; 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) { maxValue[i][j] = maxValue[i-1][j]; if(j >= weight[i]) { int tmp = maxValue[i-1][j-weight[i]] + value[i]; maxValue[i][j] = ( tmp > maxValue[i][j]) ? tmp : maxValue[i][j]; } }else { if(j >= weight[0]) maxValue[0][j] = value[0]; } } } printf("%d\n",maxValue[N-1][V]); i = N-1; j = V; while(i >= 0) { if(maxValue[i][j] == maxValue[i-1][j-weight[i]] + value[i]) { printf("%d ",i); j = j - weight[i]; } --i; } }