[bzoj1190]梦幻岛宝珠

[bzoj1190]梦幻岛宝珠


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

数据范围N<=100;W<=2^30,并且保证每颗宝石的重量符合a*2^b(a<=10;b<=30)

我们不难观察到a*2^b的范围十分特殊,所以思考如何在此基础上做优化

同时,我们观察到N与a很小,相乘也只有1000,可以作为一个合适的状态

所以我们考虑按照b来排序以此作为dp顺序,同时面对2的次方我们不难想到按二进制位dp,

而我们也不难想到我们可以很快的完成位与位之间的转移。

而参照背包问题,我们的状态便可以设计为:二进制位第i位上,我们当前还余下背包重量为 j (实际上是j*2^i)的能得到的最大总价值

我们可以推出转移方程:

//t为当前位上数字
位与位之间:f[i][j*2+t]=f[i+1][j]
同一位上:f[i][j]=max(f[i][j],f[i][j+a]+val[i])   //b==i

然后我们发现j>=1000的时候无论如何不会减小到0以下,于是我们的j只用开到1000,大于1000的直接算在1000就行了

  • 代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=110;
int n,w;
struct nd{
	int w,pw;ll v;
	bool operator<(const nd b)const{return pw<b.pw;}
}p[N];
int up[N],rst[N];
ll val[N];
void init(){
	int cnt=0;
	for(int i=n;i;i--){
		int w=p[i].w,pw=0;
		while(w%2==0){pw++;w>>=1;}
		p[i].pw=pw;
	}
	sort(p+1,p+n+1);
	for(int i=n;i;i--){
		up[++cnt]=p[i].pw;
		rst[cnt]=p[i].w>>p[i].pw;
		val[cnt]=p[i].v;
	}
}

ll f[31][1010];
int tp=0;
inline void solve(){
	int p=1;
	tp=0;
	memset(f,0,sizeof(f));
	for(int i=30;i>=0;i--){
		bool t=(1<<i)&w;
		int nwtp=min((tp<<1)+t,1000);
		for(int j=0;j<=tp;j++){
			int tgt=min((j<<1)+t,nwtp);
			f[i][tgt]=max(f[i][tgt],f[i+1][j]);
		}
		tp=nwtp;
		while(p<=n&&up[p]==i){
			int tt=rst[p];
			for(int j=0;j<=tp;j++)if(j+tt<=tp)
				f[i][j]=max(f[i][j],f[i][j+tt]+val[p]);
			p++;
		}
	}
	ll mx=0;
	for(int i=0;i<=tp;i++) mx=max(mx,f[0][i]);
	printf("%lld\n",mx);
}

int main()
{
	while(true){
		scanf("%d%d",&n,&w);
		if(n==-1)return 0;
		for(int i=1;i<=n;i++)scanf("%d%lld",&p[i].w,&p[i].v);
		init();
		solve();
	}
}

猜你喜欢

转载自blog.csdn.net/qq_35923186/article/details/83005301