【题目链接】 点击打开链接
【题意】中文题目
【解法1】 暴力。拆成01背包即可。复杂度 O(n*m*m)
int weight[110],value[110],cnt[110];
int dp[110];
int main()
{
int C;
scanf("%d",&C);
while(C--)
{
int m,n;
scanf("%d%d",&m,&n);
for(int i=0;i<n;i++)
{
scanf("%d%d%d",&value[i],&weight[i],&cnt[i]);
}
memset(dp,0,sizeof(dp));
for(int i=0;i<n;i++)
{
for(int j=0;j<cnt[i];j++)
{
for(int k=m;k>=value[i];k--)
dp[k]=max(dp[k],dp[k-value[i]]+weight[i]);
}
}
printf("%d\n",dp[m]);
}
return 0;
}
【解法2】 二进制优化。假设某个物品有10个数量,我们取数的方法是先取1个,再取2个,再取4个以此类推(i=1;i<=10;i<<=1);
也就是说,会取到1、2、4个,然后最后还会剩下3个。观察前面3个数,它们任意相加又可以组成3、5、6、7,这些数再与最后深下的3相加又可以得到8、9、10
也就是说,1到10全有了,但是我们操作的次数却从10次降低到了4次!这种优化的方法时间复杂度是O(m*∑log k[i])
int n, m, p[110], w[110], b[110], dp[110];
int main()
{
int T;
scanf("%d", &T);
while(T--)
{
scanf("%d%d", &n, &m);
REP2(i, 1, m) scanf("%d%d%d", &p[i], &w[i], &b[i]);
CLR(dp, 0);
REP2(i, 1, m){
int l = b[i];
for(int j = 1; j <= l; j <<= 1){
for(int k = n; k >= j * p[i]; k--){
dp[k] = max(dp[k], dp[k - j * p[i]] + j * w[i]);
}
l -= j;
}
if(l){
for(int k = n; k >= l * p[i]; k--){
dp[k] = max(dp[k], dp[k - l * p[i]] + l * w[i]);
}
}
}
cout << dp[n] << endl;
}
return 0;
}
【解法3】 单调队列优化
首先,对于第i件物品,如果已知体积为V,价值为W,数量为K,那么可以按照V的余数,将当前的体积J分成V组(0,1,....V-1)。
对于任意一组,可以得到转移方程:f[i*V+c]=f[k*V+c]+(i-k)*W,其中c是V组分组中的任意一个
令f[i*V+c]=dp[i],那么就得到dp[i]=dp[k]+(i-k)*W (k>=i-K)
将dp[k]-k*W看做是优化函数,那么就可以运用单调队列来优化了
struct node{
int v, pos;
node(){}
node(int v, int pos) : v(v), pos(pos) {}
}que1[110];
int dp[110];
int n, m, head1, tail1, c, w, num;
int main()
{
int T;
scanf("%d", &T);
while(T--)
{
scanf("%d%d", &n, &m);
CLR(dp, 0);
for(int i = 0; i < m; i++){
scanf("%d%d%d", &c, &w, &num);
if(n / c < num) num = n / c; //去最小的
for(int k = 0; k < c; k++){
head1 = tail1 = 0;
for(int j = 0; j <= (n - k) / c; j++){
int x = j;
int y = dp[j * c + k] - j * w;
while(head1 < tail1 && que1[head1].pos < j - num) head1++;
while(head1 < tail1 && que1[tail1 - 1].v <= y) tail1--;
que1[tail1] = node(y, x);
tail1++;
dp[j * c + k] = que1[head1].v + j * w;
}
}
}
cout << dp[n] << endl;
}
return 0;
}