目录
QUESTION:
有n件物品(每种物品都只有一件),w[i]表示物品的重量,v[i]表示物品的价值,现有一个容量为V的背包,应该如何选物品使得书包内装的物品的value之和最大呢?
解法:
二维数组:时间复杂度和空间复杂度都是O(n*V)
对于第i个物品,有选和不选两种情况
dp[i][j]表示在容量为j的情况下选取i个物品的最大value值
核心代码:
for(i=0;i<n;i++)
for(j=V;j>=w[i];j--)
dp[i][j]=max(dp[i-1][j],dp[i-1][j-w[i]]+v[i]);
一维数组:时间复杂度O(n*V),空间复杂度O(V)
核心代码:
for(i=0;i<n;i++)
for(j=V;j>=w[i];j--)
dp[j]=max(dp[j],dp[j-w[i]]+v[i]);
注意:
用二维数组存的话j的枚举是顺序还是逆序都没有关系
但是用一维数组存的话j的枚举顺序必须是逆序,即从大到小
举个例子:
V=8
i=1 | i=2 | i=3 | i=4 | |
w[i] | 2 | 3 | 4 | 5 |
v[i] | 3 | 4 | 5 | 6 |
如果按照顺序(j从小到大枚举),dp数组初始化为0
当i=1时
dp[2]=max(dp[2],dp[0]+3)=3;
dp[3]=max(dp[3],dp[1]+3)=3;
dp[4]=max(dp[4],dp[2]+3)=6;
dp[5]=max(dp[5],dp[3]+3)=6;
当容量为4时,dp[4]的值为6,它是由dp[3]+3的到的,且dp[3]=3,意思是它实际上选了两次“i=1”这个物品,那么结果显然是错的。而正巧,顺序枚举j正好完全背包问题的解法(每种物品由无穷件)。
但是如果逆序枚举的话:
dp[5]=max(dp[5),dp[3]+3);dp[5]和dp[3]之前都没有出现过,所以dp[5]=3,值是正确的(dp[3]的值的改变是在dp[5]之后)
例题:
hdoj2602:Bone Collector 典型的01背包问题
ac代码:
#include <iostream>
#include <cmath>
#include <algorithm>
#include <queue>
#include <cstring>
#define ll long long int
#define maxn 1005
using namespace std;
int v[maxn],w[maxn],dp[maxn];
int main()
{
int t,n,V,i,j,ans;
scanf("%d",&t);
while(t--)
{
memset(dp,0,sizeof(dp));
scanf("%d %d",&n,&V);
for(i=0;i<n;i++)
scanf("%d",&v[i]);
for(i=0;i<n;i++)
scanf("%d",&w[i]);
for(i=0;i<n;i++)
for(j=V;j>=w[i];j--)
dp[j]=max(dp[j],dp[j-w[i]]+v[i]);
printf("%d\n",dp[V]);
}
return 0;
}