VEXOBEN
Vexoben
Jun 15, 2018
It takes 4 minutes to read this article.

题目链接:https://www.lydsy.com/JudgeOnline/problem.php?id=1190

(刷新以获取数学公式)

题意

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

数据范围:N<=100,W<=2^30

保证每颗宝石的重量符合W=a×2^b(a<=10,b<=30)

题解

题目的限制条件很特殊,一个很自然的想法是根据对宝石进行二进制分组,将问题转化为分组背包。

组内可以进行一次01背包。表示为第组,用了的重量产生的最大价值。

难点在于怎么进行组与组之间的转移。

下设二进制的最高位位。

表示选了组到第组中的宝石,且剩余重量为的最大价值(也就是中只使用位到第位,且用到第位还留下了的重量),那么考虑转移:

1、显然有,也就是第组什么都不选。

2、枚举第组选什么,于是有

由于第一种转移的存在,第二维会达到的数量级。但是考虑到,第二维只有当小于的时候更优,所以第二维就只需要保留下的值就可以了。

#include<bits/stdc++.h>
#define LL long long
using namespace std;
const int N=1005;

int n,W,mx;
int w[N],v[N];
LL f[32][N],g[32][N];

void Init() {
	mx=0;
	for (int i=1;i<=30;i++) if ((W>>i)&1) mx=i;
	memset(f,0,sizeof(f));
	memset(g,0x80,sizeof(g));
	for (int i=1;i<=n;i++) {
		scanf("%d%d",&w[i],&v[i]);
		int bit=0,num=w[i];
		for (int j=1;j<=30;j++)	if (w[i]%(1<<j)==0) bit=j,num=w[i]/(1<<j);
		for (int j=1000;j>=num;j--) f[bit][j]=max(f[bit][j],f[bit][j-num]+v[i]);
	}
}

void Solve() {
	Init();
	g[mx+1][0]=0;
	for (int i=mx+1;i>=0;i--) {
		for (int j=0;j<=1000;j++) {
			if (g[i][j]<0) continue;
			for (int k=1;k<=j;k++) g[i][j-k]=max(g[i][j-k],g[i][j]+f[i][k]);
		}
		if (!i) continue;
		for (int j=0;j<=1000;j++) {
			int t=(j<<1)+((W>>(i-1))&1);
			if (t>1000) continue;
			g[i-1][t]=max(g[i-1][t],g[i][j]);
		}
	}
	LL ans=0;
	for (int i=0;i<=1000;i++) ans=max(ans,g[0][i]);
	printf("%lld\n",ans);
}

int main() {
	while (scanf("%d%d",&n,&W)&&(n+W>0)) Solve();
	return 0;
}