这题是看的题解= =
就是写一下做法吧
发现 “保证每颗宝石的重量符合a*2^b” 这个有些奇怪
考虑从这个条件入手,用跟二进制位有关的东西 dp
仿照 01 背包的做法,dp 的状态用容量来表示
设 f[i][j] 表示已选容量为 j * 2^i ,
并且已选容量的 2^i 之前到 2^0 的二进制位上的每一位都不超过重量上限时
所能获得的最大价值
考虑转移,选一个重量为 ai * 2^bi 的物品的转移方程是
f[bi][now] = max(f[bi][now], f[bi][now - ai] + vi)
就是跑个 01 背包
按照上面的状态定义,显然要从低位到高位 dp
也就是第一维从小到大做
上边那是同层之间的转移,根据状态定义,低位是要对高位有贡献的
考虑做完一位如何往接下来一位更新
显然二进制位之间是可以互相表示的
下面这步不太自然...
仿照 01 背包的转移,有下面伪代码
从当前层选体积为[ j ]个 2^i = 从当前层选体积为[ j - k ]个 2^i +
从下一层选[ k * 2 + 最大容量当前二进制位有1 ]个 2^(i - 1)
这道题这么写就可以过了
代码:
#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdlib>
#include <cctype>
#include <cstdio>
#include <vector>
#include <locale>
using namespace std;
typedef long long ll;
const int MAXN = 105;
int n, m;
ll ans;
int w[MAXN], v[MAXN];
ll f[32][1005];
vector<int> bel[32];
inline int rd() {
register int x = 0, c = getchar();
register bool f = false;
while (!isdigit(c)) {
f = (c == '-');
c = getchar();
}
while (isdigit(c)) {
x = x * 10 + (c ^ 48);
c = getchar();
}
return f ? -x : x;
}
inline void getbel(int No) {
register int b = 0, num = (w[No] & -w[No]);
while (!(num & 1) && num) {
num >>= 1;
++b;
}
bel[b].push_back(No);
}
inline void clearall() {
memset(f, 0, sizeof(f));
for (int i = 0 ;i <= 30; ++i) bel[i].clear();
}
int main() {
while (~(n = rd()) && ~(m = rd())) {
clearall();
for (int i = 1; i <= n; ++i) {
w[i] = rd(); v[i] = rd();
getbel(i);
}
for (int i = 0; i <= 31; ++i) {
for (int j = 0; j < (int) bel[i].size(); ++j) {
register int tmp = (w[bel[i][j]] >> i);
for (int k = 1000; k >= tmp; --k) {
f[i][k] = max(f[i][k], f[i][k - tmp] + v[bel[i][j]]);
}
}
}
for (int i = 1; i <= 31; ++i) {
for (int j = min(1000, (m >> i)); j >= 0; --j) {
for (int k = 0; k <= j; ++k) {
f[i][j] = max(f[i][j], f[i][j - k] + f[i - 1][min(1000, k * 2 + ((m >> (i - 1)) & 1))]);
}
}
}
printf("%lld
", f[31][0]);
}
return 0;
}