[HNOI2007]梦幻岛宝珠

Description

Luogu3188

BZOJ1190

Solution

最naive的想法就是直接01背包,hash一下就可以得到10分的好成绩。

考虑那个\(a*2^b\),我们可以先按\(b\)分组01背包,\(f[i][j]\)表示只用\(a*2^i\)的东西,花费\(j*2^i\)的答案。

然后合并各组物品,设\(g[i][j]\)表示背包容量为\(j*2^i\)加上\(w\)在二进制下的后\(i\)位时的收益。最后答案就是\(f[\log_2\mbox{highbit}(w)][1]\)。考虑转移,这个\(j\)有一部分是从\(f[i]\)里贡献来的,另外的是从\(g[i-1]\)里贡献的,所以:
\[ g[i][j] = \max(f[i][j-k]+g[i-1][k*2+w_i]) \]

Code

#include <algorithm>
#include <bitset>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <map>
#include <queue>
#include <set>
#include <vector>

namespace wyx {

typedef long long ll;
typedef double ld;

ll read() {
    char c;
    ll ans = 0, fl = 1;
    for (c = getchar(); c > '9' || c < '0'; c = getchar())
        if (c == '-') fl = -1;
    ans = c - '0';
    for (c = getchar(); c >= '0' && c <= '9'; c = getchar())
        ans = ans * 10 + c - '0';
    return fl * ans;
}

const int N = 105;

ll f[50][105], n, w, a[N], b[N], mx[N];
ll hd[N], nxt[N];

void pb(ll x, ll v) {
    nxt[x] = hd[v];
    hd[v] = x;
}

ll log2(ll x) {
    ll ans = -1;
    while (x) x >>= 1, ans++;
    return ans;
}

void main() {
    while (1) {
        n = read(), w = read();
        if (n == -1 && w == -1) break;
        memset(f, 0, sizeof f);
        memset(hd, 0, sizeof hd);
        memset(b, 0, sizeof b);
        memset(a, 0, sizeof a);
        memset(nxt, 0, sizeof nxt);
        memset(mx, 0, sizeof mx);
        for (int i = 1; i <= n; ++i) {
            b[i] = read();
            a[i] = read();
            int p = 0;
            while (!(b[i] & 1)) {
                b[i] >>= 1;
                p++;
            }
            pb(i, p);
            mx[p] += b[i];
        }
        for (int i = 0; i <= 30; ++i) {
            for (int j = hd[i]; j; j = nxt[j]) {
                for (int k = mx[i]; k; --k) {
                    if (k - b[j] >= 0)
                        f[i][k] = std::max(f[i][k], f[i][k - b[j]] + a[j]);
                }
            }
        }
        for (int i = 1; i <= 30; ++i) {
            mx[i] += (mx[i - 1] + 1) / 2;
            for (int j = mx[i]; j >= 0; --j) {
                for (int k = 0; k <= j; ++k) {
                    f[i][j] = std::max(
                        f[i][j],
                        f[i][j - k] +
                            f[i - 1][std::min(mx[i - 1],
                                              (k << 1) | (w >> (i - 1) & 1))]);
                }
            }
        }
        int len = 0;
        while (w >> len) len++;
        len--;
        printf("%lld\n", f[len][1]);
    }
}

}  // namespace wyx

int main() {
    wyx::main();
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/wyxwyx/p/hnoi2007mhdbz.html