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。
1 2 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 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
|
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
1 2 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; }
|