BZOJ1190: [HNOI2007]梦幻岛宝珠
这题是看的题解= =
就是写一下做法吧
发现 “保证每颗宝石的重量符合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\n", f[31][0]); } return 0; }
禁止诸如开发者知识库/布布扣/码迷/学步园/马开东等 copy 他人博文乃至博客的网站转载
,用户转载请注明出处:https://www.cnblogs.com/xcysblog/