Problem
 【HNOI2007】梦幻岛宝珠
TimeLimit:10Sec
MemoryLimit:162MB
 Description
给你N颗宝石,每颗宝石都有重量和价值。要你从这些宝石中选取一些宝石,保证总重量不超过W,且总价值最大,输出最大的总价值。
数据范围:N≤100,W≤230,且保证每颗宝石的重量可以表示为a×2b的形式(其中a≤10,b≤30)
输入文件中包含多组数据。每组数据的格式如下:第一行是两个正整数n和W,1≤n≤100,1≤W≤230,分别表示宝石的数目和最多能带走的宝石重量。接下来的n行,每行有两个正整数weighti和valuei,1≤weighti≤230,0≤valuei≤230,分别表示第i颗宝石的重量和价值,且保证weighti能写成a×2b(1≤a≤10,0≤b≤30)的形式。同一行的两个正整数之间用空格隔开。最后一组数据的后面有两个−1,表示文件的结束。这两个−1并不代表一组数据,你不需对这组数据输出结果。并且输入文件中数据的组数不超过20。
 Output
对于输入的每组数据,输出一个整数C,表示小P最多能带走的宝石的总价值。每个结果整数C单独占一行,且保证C不会超过230。
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 
 | 4 108 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
 
 | 
 Sample Output
标签:分层DP
 Solution
W很大,不能直接搞。
而保证 weight=a×2b, a≤10
因而可以按b分层后背包。
分层后先层内DP出f[i][j]表示2i层级内容量为2i×j的最大价值。
然后层间DP求最大值:f[i][j]=max(f[i][j],f[i][k]+f[i−1][(j−k)×2+(w>>i−1)&1])
详见代码。
 Code
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 
 | #include <bits/stdc++.h>#define LOG 30
 #define MAX_N 100
 using namespace std;
 int n, m, l, w[MAX_N+5][LOG+5], c[MAX_N+5][LOG], f[LOG+5][MAX_N+5], cnt[LOG+5], s[LOG+5];
 int main() {
 while (scanf("%d%d", &n, &m) && ~n && ~m) {
 memset(cnt, 0, sizeof cnt), memset(s, 0, sizeof s), memset(f, 0, sizeof f), l = 0;
 while (n--) {
 int x;	scanf("%d", &x);
 int t = 0;	while (!(x&1)) t++, x >>= 1;
 w[t][++cnt[t]] = x, s[t] += x;
 l = max(l, t), scanf("%d", &c[t][cnt[t]]);
 }
 for (int i = 0; i <= l; i++) for (int j = 1; j <= cnt[i]; j++)
 for (int k = s[i]; k >= w[i][j]; k--) f[i][k] = max(f[i][k], f[i][k-w[i][j]]+c[i][j]);
 while (m >> l) l++;	l--;
 for (int i = 1; i <= l; i++) {
 s[i] += (s[i-1]+1)>>1;
 for (int j = s[i]; ~j; j--) for (int k = 0; k <= j; k++)
 f[i][j] = max(f[i][j], f[i][j-k]+f[i-1][min(s[i-1], (k<<1)|((m>>(i-1))&1))]);
 }
 printf("%d\n", f[l][1]);
 }
 return 0;
 }
 
 |