【题目链接】 点击打开链接

【题意】中文题目


【解法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;
}