[Luogu P3188] [HNOI2007]梦幻岛宝珠

版权声明:欢迎转载蒟蒻博客,但请注明出处: https://blog.csdn.net/LPA20020220/article/details/82930163

洛谷传送门

BZOJ传送门

题目描述

给你 N N 颗宝石,每颗宝石都有重量和价值。要你从这些宝石中选取一些宝石,保证总重量不超过 W W ,且总价值最大为,并输出最大的总价值。数据范围: N 100 ; W 2 30 N\le 100;W\le 2^{30} ,并且保证每颗宝石的重量符合 a 2 b a*2^b a 10 ; b 30 a\le 10;b\le 30

输入输出格式

输入格式:

输入文件中包含多组数据。每组数据的格式如下:第一行是两个正整数 n n W W 1 n 100 , 1 W 2 30 1\le n\le 100,1\le W\le 2^{30} ,分别表示宝石的数目和最多能带走的宝石重量。接下来的 n n 行,每行有两个正整数 w e i g h t i weight_i v a l u e i value_i 1 w e i g h t i 2 30 , 0 v a l u e i 2 30 1\le weight_i\le 2^{30}, 0\le value_i\le 2^{30} ,分别表示第 i i 颗宝石的重量和价值,且保证 w e i g h t i weight_i 能写成 a 2 b a*2^b ( 1 a 10 , 0 b 30 1\le a\le 10,0\le b\le 30 )的形式。同一行的两个正整数之间用空格隔开。最后一组数据的后面有两个 1 -1 ,表示文件的结束。这两个 1 -1 并不代表一组数据,你不需对这组数据输出结果。并且输入文件中数据的组数不超过 20 20

输出格式:

对于输入的每组数据,输出一个整数 C C ,表示小P最多能带走的宝石的总价值。每个结果整数 C C 单独占一行,且保证 C C 不会超过 2 30 2^{30}

输入输出样例

输入样例#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

解题分析

玄妙的状态转移。

直接做背包显然会 M L E + T L E MLE+TLE , 我们考虑分层, d p [ i ] [ j ] dp[i][j] 表示只用可以表示为 k × 2 i k\times 2^i 的物品, 容量为 2 j 2^j 的情况下的最大收益。这时候我们预处理出 t o t [ i ] tot[i] 表示第 i i 层的最大重量。

然后我们考虑分层转移, d p [ i ] [ j ] dp[i][j] 表示容量为 j × 2 i + W j\times 2^i+W i i 位之后的重量可以得到的最大收益, 可以得到转移方程为
t o t [ i ] = t o t [ i ] + t o t [ i 1 ] 2 d p [ i ] [ j ] = m a x k = 1 j ( d p [ i ] [ j k ] + d p [ i 1 ] [ m i n ( k × 2 + ( W > > i 1 )   m o d   2 , 1000 ) ] ) tot[i]= tot[i]+\lceil\frac{tot[i-1]}{2}\rceil \\ dp[i][j]=max_{k=1}^{j}(dp[i][j-k]+dp[i-1][min(k\times2+(W>>i-1)\ mod\ 2,1000)])
记录每一层的 t o t tot 可以大幅优化复杂度, 这样就能过了。

代码如下:

#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]);
	}
}

猜你喜欢

转载自blog.csdn.net/LPA20020220/article/details/82930163