如果只使用一维数组f[0…v],我们要达到的效果是:第i次循环结束后f[v]中所表示的就是使用二维数组时的f[i][v],即前i个物体面对容量v时的最大价值。我们知道f[v]是由两个状态得来的,f[i-1][v]和f[i-1][v-c[i]],使用一维数组时,当第i次循环之前时,f[v]实际上就是f[i-1][v],那么怎么得到第二个子问题的值呢?事实上,如果在每次循环中我们以v=v…0的顺序推f[v]时,就能保证f[v-c[i]]存储的是f[i-1][v-c[i]]的状态。状态转移方程为:
1
2
|
v = V...0; f(v) = max{ f(v), f(v-c[i])+w[i] }
|
我们可以与二维数组的状态转移方程对比一下
1
2
|
f(i,v) = max{ f(i-1,v), f(i-1,v-c[i])+w[i] }
|
正如我们上面所说,f[v-c[i]]就相当于原来f[i-1][v-c[i]]的状态。如果将v的循环顺序由逆序改为顺序的话,就不是01背包了,就变成完全背包了,这个后面说。这里举一个例子理解为何顺序就不是01背包了
假设有物体z容量2,价值vz很大,背包容量为5,如果v的循环顺序不是逆序,那么外层循环跑到物体z时,内循环在v=2时,物体z被放入背包,当v=4时,寻求最大价值,物体z放入背包,f[4]=max{f[4],f[2]+vz},这里毫无疑问后者最大,那么此时f[2]+vz中的f[2]已经装入了一次物体z,这样一来该物体被装入背包两次了就,不符合要求,如果逆序循环v,这一问题便解决了。
#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]);
}
http://www.cnblogs.com/daoluanxiaozi/archive/2012/05/06/2486105.html