来自《挑战程序设计竞赛》
1.题目原文
有n中物品,它们的重量和价值分别是w[i]和v[i]。现在要从中选出一些物品使得总重量不超过W,并且价值的总和最大。不过要求第i中物品最多可以选m[i]个。
数据范围
1<=n<=100,1<=w[i],v[i]<=100,1<=m[i]<=10000,1<=W<=10000
2.解题思路1
这是一个有个数限制的问题,对于每个物品至多选一个或者可以选任意个的问题我们已经在O(nW)时间内求解。如果使用同样的方法解答本题,则状态转移方程为:
dp[i][j]:从编号为0到编号为i-1,前i个物品总重量不超过j的所有选法中最大可能的价值。
有dp[0][j]=0;dp[i+1][j]=max{dp[i][j-k*w[i]]+k*v[i]|0<=k<=m[i]且j-k*w[i]>=0}。
如果使用这个状态转移方程,时间复杂度就是O(nmW)。
3.代码1
int n,W;
int w[maxn],v[maxn],m[maxn];
int dp[maxn][maxW];
void solve()
{
memset(dp,0,sizeof(dp));
for(int i=0;i<=W;i++){
dp[0][i]=0;
}
for(int i=0;i<n;i++){
for(int j=0;j<=W;j++){
for(int k=0;k<=m[i]&&j>=k*w[i];k++){
dp[i+1][j]=max(dp[i+1][j],dp[i][j-k*w[i]]+k*v[i]);
}
}
}
printf("%d\n",dp[n][W]);
}
这个算法时间复杂度较高,无法在规定时间内求解。4.解题思路2
5.代码2
6.解题思路3
我们把m[i]分解成如下形式:m[i]=1+2+2^2+……2^k+a(0<=a<2^(k+1))
由于1,2,2^2,……2^k可以表示出0~2^(k+1)-1的所有整数,因此,1,2,2^2,……2^k,a可以表示出0~m[i]的所有整数。因此,我们可以把m[i]个重量和价值分别是w[i],v[i]的物品看成重量和价值分别是w[i]*x,v[i]*x(x=1,2,2^2,……,2^k,a)的k+2个物品,这样物品的总数就变为O(nlogm)个,使用一般的01背包DP可以在O(nWlogm)时间内求出答案。
7.代码3
int n,W;
int w[maxn],v[maxn],m[maxn];
int dp[maxW];
void solve()
{
for(int i=0;i<n;i++){
int num=m[i];
for(int k=1;num>0;k<<=1){
int mul=min(k,num);
for(int j=W;j>=w[i]*mul;j--){
dp[j]=max(dp[j],dp[j-w[i]*mul]+v[i]*mul);
}
num-=mul;
}
}
printf("%d\n",dp[W]);
}