梦幻岛宝珠题解

圆题链接

01背包冲鸭!!!

好吧他死了,考虑每一个物品体积都为\(b*2^a\)尝试进制优化
首先设背包大小为\(m\)
\(f_{i,j}\)为背包大小最高位为\(j*2^i\)时最大价值
也就是说,设现在考虑的物品编号为\(k\),则背包大小为\(j*2^i\)+\((\)\(w_k\)&\(((1<<i)-1)))\)
原来我们是枚举整个背包大小,而现在我们不再考虑低于第\(i\)位的确切数值
比如当\(w_k=1001\)\(i=5,j=1\),背包大小为\(11001\)(除了\(i\),以上均为二进制)
转移则为\(f_{i,j}=max(f_{i,j},f_{i,j-d}+f{i-1,2*d|((m>>(i-1))-1)})\)
就是说考虑从\(i-1\)处转移过来\(2^i*d\)大小的体积,由于\(i\)减了\(1\)\(d\)相应要乘\(2\)
就是\(f_{i-1,2*d}\),考虑背包自身上限,还可以加上一个\(2*d+((m>>(i-1))-1\)

#include<iostream>
#include<cstdio>
#include<cstring>
#define ll long long
using namespace std;
const ll N=1005;
ll n,m,f[35][N],a[N],b[N],top;

int main(){
	scanf("%lld%lld",&n,&m);
	while(n!=-1&&m!=-1){
		top=0;
		memset(f,0,sizeof f);
		for(ll i=1;i<=n;i++){
			ll cnt=0,x;
			scanf("%lld%lld",&a[i],&b[i]),x=a[i];
			while(x%2==0){x/=2,cnt++;}
			for(ll j=1000;j>=x;j--){
				f[cnt][j]=max(f[cnt][j],f[cnt][j-x]+b[i]);
			}
		}
		for(ll i=m;i>0;i/=2)top++;
		top--;
		for(ll i=1;i<=top;i++){
			for(ll j=1000;j>=0;j--){
				for(ll k=0;k<=j;k++){
					f[i][j]=max(f[i][j],f[i][j-k]+f[i-1][min(1000*1ll,(k*2)|((m>>(i-1))&1))]);
				}
			}
		} 
		printf("%lld\n",f[top][1]);
		scanf("%lld%lld",&n,&m); 
	}
}

代码比较憨,请见谅

posted @ 2020-10-17 23:27  蒟蒻丁  阅读(104)  评论(0编辑  收藏  举报