版权声明:欢迎转载蒟蒻博客,但请注明出处: https://blog.csdn.net/LPA20020220/article/details/82930163
洛谷传送门
BZOJ传送门
题目描述
给你 颗宝石,每颗宝石都有重量和价值。要你从这些宝石中选取一些宝石,保证总重量不超过 ,且总价值最大为,并输出最大的总价值。数据范围: ,并且保证每颗宝石的重量符合 ( )
输入输出格式
输入格式:
输入文件中包含多组数据。每组数据的格式如下:第一行是两个正整数 和 , ,分别表示宝石的数目和最多能带走的宝石重量。接下来的 行,每行有两个正整数 和 , ,分别表示第 颗宝石的重量和价值,且保证 能写成 ( )的形式。同一行的两个正整数之间用空格隔开。最后一组数据的后面有两个 ,表示文件的结束。这两个 并不代表一组数据,你不需对这组数据输出结果。并且输入文件中数据的组数不超过 。
输出格式:
对于输入的每组数据,输出一个整数 ,表示小P最多能带走的宝石的总价值。每个结果整数 单独占一行,且保证 不会超过 。
输入输出样例
输入样例#1:
4 10
8 9
5 8
4 6
2 5
4 13
8 9
5 8
4 6
2 5
16 75594681
393216 5533
2 77
32768 467
29360128 407840
112 68
24576 372
768 60
33554432 466099
16384 318
33554432 466090
2048 111
24576 350
9216 216
12582912 174768
16384 295
1024 76
-1 -1
输出样例#1:
14
19
1050650
解题分析
玄妙的状态转移。
直接做背包显然会 , 我们考虑分层, 表示只用可以表示为 的物品, 容量为 的情况下的最大收益。这时候我们预处理出 表示第 层的最大重量。
然后我们考虑分层转移,
表示容量为
第
位之后的重量可以得到的最大收益, 可以得到转移方程为
记录每一层的
可以大幅优化复杂度, 这样就能过了。
代码如下:
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <algorithm>
#include <cctype>
#include <cmath>
#define R register
#define IN inline
#define W while
#define gc getchar()
#define MX 1005
#define ll long long
bool neg;
template <class T>
IN void in(T &x)
{
x = 0; R char c = gc;
for (; !isdigit(c); c = gc)
if(c == '-') neg = true;
for (; isdigit(c); c = gc)
x = (x << 1) + (x << 3) + c - 48;
if(neg) neg = false, x = -x;
}
ll dp[32][MX];
int tot[32];
int num, cst;
int main(void)
{
R int i, j, k, tar, bd, v;
W (233)
{
in(num), in(cst);
if(num < 0) break;
std::memset(dp, 0, sizeof(dp));
std::memset(tot, 0, sizeof(tot));
for (i = 1; i <= num; ++i)
{
in(tar); in(v); k = 0;
W (!(tar & 1)) ++k, tar >>= 1;
tot[k] += tar;
for (j = 1000; j >= tar; --j)
dp[k][j] = std::max(dp[k][j], dp[k][j - tar] + v);
}
bd = 0; int tmp = cst;
W (tmp) tmp >>= 1, bd++; bd--;
for (i = 1; i <= bd; ++i)
{
tot[i] += tot[i - 1] + 1 >> 1;
for (j = tot[i]; ~j; --j)
for (k = 0; k <= j; ++k)
dp[i][j] = std::max(dp[i][j], dp[i][j - k] + dp[i - 1][std::min(tot[i - 1], k << 1 | ((cst >> i - 1) & 1))]);
}
printf("%lld\n", dp[bd][1]);
}
}