题目链接:https://vjudge.net/contest/346265#problem/H
这是一道有个数限制的背包问题。
对于每一种物品至多选一个或者选任意个的问题我们已经可以在**O(nW)**时间内求解。
对于此问题,时间复杂度O(nWc)
代码:
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int n,m;
int p[101],h[102],c[101];
int dp[101];
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
memset(dp,0,sizeof(dp));
scanf("%d%d",&n,&m);
for(int i=0; i<m; i++)
scanf("%d%d%d",&p[i],&h[i],&c[i]);
for(int i=0; i<m; i++)
{
for(int j=n; j>=0; j--)
{
for(int k=0; k<=c[i]; k++)
{
if(k*p[i]>j)break;
dp[j]=max(dp[j],dp[j-k*p[i]]+k*h[i]);
}
}
}
printf("%d\n",dp[n]);
}
return 0;
}
优化:
每一种物品至多选mi个。
mi=1+2+…+2^k+a(0<=a<2 ^(k+1))
由于1,2…,2^(k+1)的组合可以表示0~2 ^(k+1)-1的所有整数,因此1,2…2 ^k,a可以表示0~m的所有整数。
我们把mi个重量和价值分别为wi和vi的物品,看成重量和价值分别为wi * x,vi * x(x=1,2…2 ^k,a)的k+2个物品。
这样物品的总个数就变为(nlogm)个,使用一般的01背包可以在O(nWlogm)时间内求解。
代码:
#include<cstring>
#include<algorithm>
using namespace std;
const int N=1e3+10;
int n,m;
int p[N],h[N],c[N];
int dp[N];
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
memset(dp,0,sizeof(dp));
scanf("%d%d",&n,&m);
for(int i=0; i<m; i++)
scanf("%d%d%d",&p[i],&h[i],&c[i]);
for(int i=0; i<m; i++)
{
int num=c[i];
for(int k=1; num>0; k<<=1)
{
int mul=min(k,num);
for(int j=n; j>=p[i]*mul; j--)
dp[j]=max(dp[j],dp[j-p[i]*mul]+h[i]*mul);
num-=mul;
}
}
printf("%d\n",dp[n]);
}
return 0;
}