洛谷传送门
BZOJ传送门
题目描述
给你 N N N颗宝石,每颗宝石都有重量和价值。要你从这些宝石中选取一些宝石,保证总重量不超过 W W W,且总价值最大为,并输出最大的总价值。数据范围: N ≤ 100 ; W ≤ 2 30 N\le 100;W\le 2^{30} N≤100;W≤230,并且保证每颗宝石的重量符合 a ∗ 2 b a*2^b a∗2b( a ≤ 10 ; b ≤ 30 a\le 10;b\le 30 a≤10;b≤30)
输入输出格式
输入格式:
输入文件中包含多组数据。每组数据的格式如下:第一行是两个正整数 n n n和 W W W, 1 ≤ n ≤ 100 , 1 ≤ W ≤ 2 30 1\le n\le 100,1\le W\le 2^{30} 1≤n≤100,1≤W≤230,分别表示宝石的数目和最多能带走的宝石重量。接下来的 n n n行,每行有两个正整数 w e i g h t i weight_i weighti和 v a l u e i value_i valuei, 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} 1≤weighti≤230,0≤valuei≤230,分别表示第 i i i颗宝石的重量和价值,且保证 w e i g h t i weight_i weighti能写成 a ∗ 2 b a*2^b a∗2b( 1 ≤ a ≤ 10 , 0 ≤ b ≤ 30 1\le a\le 10,0\le b\le 30 1≤a≤10,0≤b≤30)的形式。同一行的两个正整数之间用空格隔开。最后一组数据的后面有两个 − 1 -1 −1,表示文件的结束。这两个 − 1 -1 −1并不代表一组数据,你不需对这组数据输出结果。并且输入文件中数据的组数不超过 20 20 20。
输出格式:
对于输入的每组数据,输出一个整数 C C C,表示小P最多能带走的宝石的总价值。每个结果整数 C C C单独占一行,且保证 C C C不会超过 2 30 2^{30} 230。
输入输出样例
输入样例#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 MLE+TLE, 我们考虑分层, d p [ i ] [ j ] dp[i][j] dp[i][j]表示只用可以表示为 k × 2 i k\times 2^i k×2i的物品, 容量为 2 j 2^j 2j的情况下的最大收益。这时候我们预处理出 t o t [ i ] tot[i] tot[i]表示第 i i i层的最大重量。
然后我们考虑分层转移,
d
p
[
i
]
[
j
]
dp[i][j]
dp[i][j]表示容量为
j
×
2
i
+
W
j\times 2^i+W
j×2i+W第
i
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)])
tot[i]=tot[i]+⌈2tot[i−1]⌉dp[i][j]=maxk=1j(dp[i][j−k]+dp[i−1][min(k×2+(W>>i−1) mod 2,1000)])
记录每一层的
t
o
t
tot
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]);
}
}