题目:有价值为v[i]、重量为w[i]的N个物品(每个物品仅有一个)及容量为W的背包,现要求在物品总重量不超过W的前提下选择总价值尽可能高的物品放进背包里。
分析:用一个二维数组T[i][j]表示背包的容量为j,物品数为1~i时的最大价值,T[i][j]只有选择第i种物品或者不选第i种物品两种可能,因此状态方程可写成T[i][j]=max(T[i-1][j],T[i-1][j-w[i]]+v[i]),其中前一种是不选第i种物品,后一种是选择第i种物品。
这种方法时间和空间复杂度都是O(N*W), 空间复杂度可以优化到O(W), 即使用一维数组, 代码如下:
for(int i = 1; i <= N; i++)
{
for(int j = W; j >= w[i]; j--)
{
T[j]=max(T[j],T[j-w[i]]+v[i]);
}
}
一般求最优解有两种问法: 恰好装满背包和重量不超过背包,而它们的初始化是不同的。
1.恰好装满背包,除了T[0]=0外T[1....W]均为-∞, 如果不能恰好装满背包,则T[W] =-∞ , 则说明没有最优解。
2.重量不超过背包,T[0....W]均初始化为0。
附上题目代码:
#include<iostream>
#include<algorithm>
using namespace std;
const int Max=1000+5;
int w[Max],v[Max],T[Max];
int main()
{
int N,W;
cin>>N>>W;
for(int i=1;i<=N;i++)
{
cin>>v[i]>>w[i];
for(int i=0;i<=W;i++)T[i]=0;
for(int i=1;i<=N;i++)
for(int j=W;j>=w[i];j--)
{
T[j]=max(T[j],T[j-w[i]]+v[i]);
}
cout<<T[W]<<endl;
return 0;
}
把题目改为恰好装满背包,代码如下(注意与上述代码不同之处)
#include<iostream>
#include<algorithm>
using namespace std;
const int Max=1000+5;
const int INFTY=-1000000;
int w[Max],v[Max],T[Max];
int main()
{
int N,W;
cin>>N>>W;
for(int i=1;i<=N;i++)
{
cin>>v[i]>>w[i];
}
for(int i=1;i<=W;i++)T[i]=INFTY;
T[0]=0;
for(int i=1;i<=N;i++)
for(int j=W;j>=w[i];j--)
{
T[j]=max(T[j],T[j-w[i]]+v[i]);
}
if(T[W]<0)
cout<<"It's impossible!"<<endl;
else
cout<<T[W]<<endl;
return 0;
}
题目再次修改,如果每件物品都有无穷多件,则为完全背包问题。每种物品有多种选择,选0件、1件……n件(不超过背包能装的重量), 用二维数组表示T[i][j]=max(T[i-1][j-k*w[i]] + k*v[i]), 0<= k*w[i]<= W。实际上,确是直接使用一维数组,即如果当前背包未满,继续往里装第i种物品。
for(int i=1;i<=N;i++)
for(int j=w[i];j<=W;j++)
{
T[j]=max(T[j],T[j-w[i]]+v[i];
}
发现完全背包问题跟01背包的区别在于内层循环的顺序不同。
例题1:01背包 HDU2602
例题2:完全背包 HDU1114