01背包问题描述:给定N个体积为V1....Vn,价值为P1...Pn的物品和一个体积为V的包,求能够装进包中物品的最大价值。
为了设计一个动态规划算法,我们需要导出状态转移方程。先考虑原问题的子问题,假设我们考虑前i个问题,背包容积为j,记将前i个物品装到容积为j的包中最大总价值为W[i,j];每个物品只有选择和不选择两种情况,我们根据第i个物品是否选择进行分类。在不包含第i个物品的最优解价值为W[i-1,j],在包含第i个物品的最优解中,最优子集是该物品和前i-1个物品中能够放入最大体积 j - v[i](j-v[i]>0)的背包的最优解组成。这时候最优解为p[i]+W[i-1,j-v[i]];于是在前i个物品的最优解是这两种情况的最大值。特殊情况,第i个物品本身体积大于包时,从前i个物品中选出的最优解价值等于从前i-1个物品中选出的最优解的价值。
状态转移方程:W[i,j]=max{ W[i-1][ j ] , W[ i-1][ j-v[i]]+p[ i] }(j-v[i]>=0),若j-v[i]<0,则W[i,j]=W[i-1,j].
上面状态转移方程时间和空间复杂度均为O(VN),时间复杂度无法优化,而空间复杂度还可以继续优化。我们可以使用一维数组来代替上面的二维数组,则状态转移方程如下:
W[ j ]=max{W[ j ],W[j-v[ i ]]+p[ i ]} (j>=v[ i ])
其中左边的W[j]是W[i,j],右边的W[j]是W[i-1,j],W[j-v[i]]是W[i-1][j-v[i]].可以用下面循环:
for(i=1;i<=N;i++)
for(j=V;j>=v[i];j--)
......
这样一来空间复杂度为O(V);我们再来看初始化的问题,背包问题有两种情况。第一种要求背包装满,在初始化时W[0]=0,而W[1..V]= -1,因为在0个物品情况下,只有体积为0的背包可以看做什么也不放入即是他的合法解,而包体积j>0时是无论如何也装不满的,于是找不到合法的解,标志为-1;第二种是不要求背包装满的情况,则可以将W[0...V]=0全部初始化为0,这时有0个物品,包里什么也不装就是它的合法解,总价值都为0;下面两种情况C++代码:
</pre><pre name="code" class="cpp">#include<iostream>
using namespace std;
#define MAX_SIZE 500
int N, V;
int W[MAX_SIZE];
int p[MAX_SIZE];
int v[MAX_SIZE];
int main(){
int i,j,pos;
memset(W, -1, sizeof(W)); //边界初始化
W[0] = 0;
cin >> N >>V;
for (i = 1; i <= N; i++)
cin >> v[i] >> p[i];
for (i = 1; i <= N; i++){
for (j = V; j >= v[i]; j--){
pos = j - v[i];
if (W[pos] != -1 && W[pos]+p[i] > W[j])
W[j] = W[pos] + p[i];
}
}
cout << W[V] << endl;
return 0;
}</span>
#include<iostream>
using namespace std;
#define MAX_SIZE 500
int N, V;
int W[MAX_SIZE];
int p[MAX_SIZE];
int v[MAX_SIZE];
int Bag01(int v[], int p[]);
int main(){
int i,j,pos;
memset(W,0, sizeof(W)); //边界初始化
cin >> N >>V;
for (i = 1; i <= N; i++)
cin >> v[i] >> p[i];
for (i = 1; i <= N; i++){
for (j = V; j >= v[i]; j--){
pos = j - v[i];
if ( W[j]<W[pos]+p[i])
W[j] = W[pos] + p[i];
}
}
cout << W[V] << endl;
return 0;
}</span>