【题解】HEOI2013Eden 的新背包问题

  这题真的神奇了……蜜汁复杂度(`・ω・´)

  应该是一个比较连贯的思维方式:去掉一个物品,那么我们转移的时候不考虑它就好了呗。考虑暴力:每一次都对剩余的n - 1个物品进行多重背包转移,获得答案。既然可以优化,就说明一定有重复计算的地方——画出一张方格图,把不需要的格子涂掉——我们突然发现每一个可以有两部分组成,而两部分可以递推得到!那就很简单了:A[i][]表示选择n ~ i 这些物品能获得的最大值,B[i][]表示选择1~i的物品所能获得的最大值。

  答案就是两部分的AB数组暴力合并即可。

#include<bits/stdc++.h>
using namespace std;
#define maxn 1500
#define maxq 300015
int n, q, W[maxn], V[maxn], w[maxn * 10], v[maxn * 10], T[maxn];
int cnt, L[maxn], R[maxn], A[maxn][maxn], B[maxn][maxn];
int M;

struct que
{
    int num, id, m;
}Q[maxq];

int read()
{
    int x = 0, k = 1;
    char c;
    c = getchar();
    while(c < '0' || c > '9') { if(c == '-') k = -1; c = getchar(); }
    while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
    return x * k;
}

void Get_A()
{
    for(int i = n; i >= 1; i --)
    {
        int s = T[i], t = 1;
        L[i] = cnt + 1;
        while(s >= t)
        {
            w[++ cnt] = W[i] * t;
            v[cnt] = V[i] * t;
            s -= t, t *= 2;
        }
        if(s) 
        {
            w[++ cnt] = W[i] * s;
            v[cnt] = V[i] * s;
            s -= t, t *= 2;
        }
        R[i] = cnt;
        memcpy(A[i], A[i + 1], sizeof(A[i + 1]));
        for(int j = L[i]; j <= R[i]; j ++)
            for(int k = M; k >= w[j]; k --)
                A[i][k] = max(A[i][k], A[i][k - w[j]] + v[j]);            
    }
}

void Get_B()
{
    for(int i = 1; i < n; i ++)
    {
        memcpy(B[i], B[i - 1], sizeof(B[i - 1]));
        for(int j = L[i]; j <= R[i]; j ++)
            for(int k = M; k >= w[j]; k --)
                B[i][k] = max(B[i][k], B[i][k - w[j]] + v[j]);
    }
}

int main()
{
    n = read();
    for(int i = 1; i <= n; i ++)
        W[i] = read(), V[i] = read(), T[i] = read();
    q = read();
    for(int i = 1; i <= q; i ++)
    {
        Q[i].num = read() + 1, Q[i].m = read();
        Q[i].id = i;
        M = max(Q[i].m, M);
    }
    Get_A();
    Get_B();
    for(int i = 1; i <= q; i ++)
    {
        int k1 = Q[i].num + 1, k2 = Q[i].num - 1;
        int j = Q[i].m, ans = 0;
        for(int a1 = 0; a1 <= j; a1 ++)
            ans = max(ans, A[k1][a1] + B[k2][j - a1]);
        printf("%d\n", ans);
    }
    return 0;
} 

 

posted @ 2018-05-05 10:59  Twilight_Sx  阅读(239)  评论(0编辑  收藏  举报