欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

[HNOI2007]梦幻岛宝珠 DP+折半

程序员文章站 2024-03-20 18:16:22
...

Description
给你N颗宝石,每颗宝石都有重量和价值。要你从这些宝石中选取一些宝石,保证总重量不超过W,并输出最大的总价值。


Sample Input
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
14
19
1050650


我们考虑折半一下。
将b大于15次方的数变成a2b16a*2^{b-16},然后折半去做DP。
我们发现这样空间和时间都会炸锅。
于是我们可以调整一下折半的分界线,我调到10就过了。(感觉我是卡过去的)


#include <ctime>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <algorithm>

using namespace std;
typedef long long LL;
const int maxn = 9 * ((1 << 16) - 1);
LL _max(LL x, LL y) {return x > y ? x : y;}
int _min(int x, int y) {return x < y ? x : y;}
int read() {
	int s = 0, f = 1; char ch = getchar();
	while(ch < '0' || ch > '9') {if(ch == '-') f = -1; ch = getchar();}
	while(ch >= '0' && ch <= '9') s = s * 10 + ch - '0', ch = getchar();
	return s * f;
}

struct node {
	int a, b, v;
} w1[110], w2[110]; int n1, n2;
int bin[30];
LL f1[1000000], f2[550000];

int main() {
	bin[0] = 1; for(int i = 1; i <= 30; i++) bin[i] = bin[i - 1] * 2;
	while(1) {
		int n = read(), m = read();
		if(n == -1 && m == -1) break;
		n1 = n2 = 0; int s1 = 0, s2 = 0;
		for(int i = 1; i <= n; i++) {
			int w = read(), v = read();
			int b = 0;
			while(w % 2 == 0) w /= 2, ++b;
			if(b > 10) w1[++n1].a = w, w1[n1].b = b - 11, w1[n1].v = v, s1 += w1[n1].a * bin[w1[n1].b];
			else w2[++n2].a = w, w2[n2].b = b, w2[n2].v = v, s2 += w2[n2].a * bin[w2[n2].b];
		} memset(f1, -1, sizeof(f1));
		f1[0] = 0; s1 = _min(s1, m / bin[11]);
		for(int i = 1; i <= n1; i++) {
			int c = w1[i].a * bin[w1[i].b];
			for(int j = s1 - c; j >= 0; j--) if(f1[j] != -1){
				f1[j + c] = _max(f1[j + c], f1[j] + w1[i].v);
			}
		} memset(f2, -1, sizeof(f2));
		f2[0] = 0;
		for(int i = 1; i <= n2; i++) {
			int c = w2[i].a * bin[w2[i].b];
			for(int j = s2 - c; j >= 0; j--) if(f2[j] != -1){
				f2[j + c] = _max(f2[j + c], f2[j] + w2[i].v);
			}
		} LL ans = 0;
		for(int i = 0; i <= s2; i++) f2[i] = _max(f2[i], f2[i - 1]);
		for(int i = 0; i <= s1; i++) {
			if(m < (LL)i * bin[11]) break;
			if(f1[i] != -1) ans = _max(ans, f1[i] + f2[_min(s2, m - i * bin[11])]);
		} printf("%lld\n", ans);
	}
	return 0;
}

相关标签: DP 折半