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

48 篇文章 0 订阅
1 篇文章 0 订阅

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次方的数变成 a ∗ 2 b − 16 a*2^{b-16} a2b16,然后折半去做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;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值