P3188 [HNOI2007] 梦幻岛宝珠

标签:DP) \(C\)

容易想到按 \(b_i\) 给物品分组。

对每个组跑背包,设 \(f_{i,j}\) 表示只考虑重量为 \(a \times 2^i\) 的物品,用 \(j × 2^i\) 的体积装的最大价值,这个显然是 01 背包 板子。

然后考虑背包的合并,设 \(g_{i,j}\) 表示考虑重量为 \(a \times 2^0 \sim a \times 2^i\) 的物品,用了 \(j × 2^i + m\&(2^i − 1)\) 的体积的最大价值,转移同样是显然的。

#include<bits/stdc++.h>
using namespace std;
const int NN = 108;
int n,m;
int num[32];
int a[32][NN];
int w[32][NN];
int f[32][1008];
int g[32][1008];
inline int read(){
	int res = 0,flag = 1;
	char c = getchar();
	while(!isdigit(c)){
		if(c == '-') flag = -1;
		c = getchar();
	}
	while(isdigit(c))
		res = res * 10 + c - '0',c = getchar();
	return res * flag;
}
void init(){
	memset(a,0,sizeof(a));
	memset(num,0,sizeof(num));
	memset(w,0,sizeof(w));
	memset(f,0,sizeof(f));
	memset(g,0,sizeof(g));
}
int main(){
	n = read(); m = read();
	while(n != -1 && m != -1){
		init();
		for(int i = 1; i <= n; ++i){
			int x = read();
			int cnt = 31;
			while(x & ((1 << (--cnt)) - 1));
			a[cnt][++num[cnt]] = x / (1 << cnt);
			w[cnt][num[cnt]] = read();
		}
		int cnt = 31;
		while(m <= (1 << (--cnt)));
		for(int t = 0; t <= cnt; ++t)
			for(int i = 1; i <= num[t]; ++i)
				for(int j = 10 * num[t]; j >= a[t][i]; --j)
					f[t][j] = max(f[t][j],f[t][j - a[t][i]] + w[t][i]);
		
		for(int j = 0; j <= 10 * num[0]; ++j)
			g[0][j] = f[0][j];
		
		for(int i = 1; i <= cnt; ++i)
			for(int j = 0; j <= 10 * n; ++j)
				for(int k = 0; k <= j; ++k)
					g[i][j] = max(g[i][j],f[i][j - k] + g[i - 1][min(10 * n,k * 2 + ((m >> (i - 1)) & 1))]);
		
		printf("%d\n",g[cnt][1]);
		n = read(),m = read();
	}
}
posted @ 2024-02-04 20:51  ricky_lin  阅读(10)  评论(0)    收藏  举报