Description
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_2mbox{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
", f[len][1]);
}
}
} // namespace wyx
int main() {
wyx::main();
return 0;
}