[HNOI2007]梦幻岛宝珠(背包)
给你N颗宝石,每颗宝石都有重量和价值。要你从这些宝石中选取一些宝石,保证总重量不超过W,且总价值最大为,并输出最大的总价值。数据范围:N<=100;W<=2^30,并且保证每颗宝石的重量符合a*2^b(a<=10;b<=30)
Solution
神仙背包。
我们可以先对每个二进制位dp一下,然后从低到高位依次处理。
合并操作比较麻烦。
假设我们有一个容量为j的背包,我们要从上一层拿容量为k的物品,那么我们上一层需要的大小为k*2+(w>>i-1)&1,后面的是要满足w的二进制位,前面的是要贡献上去。
Code
#include<iostream> #include<cstdio> #include<cstring> #include<cmath> #define N 32 using namespace std; int dp[N][1002],e[N][102],val[N][102],ji[N],sum[N],n,w,ww,v,ans; const int ma=1000; int main(){ while(1){ scanf("%d%d",&n,&w); if(n<0)break; memset(dp,0,sizeof(dp));memset(e,0,sizeof(e));memset(val,0,sizeof(val)); memset(ji,0,sizeof(ji));memset(sum,0,sizeof(sum)); ans=0; for(int i=1;i<=n;++i){ scanf("%d%d",&ww,&v); for(int j=30;j>=0;--j) if(ww%(1ll<<j)==0){ e[j][++ji[j]]=ww/(1ll<<j),sum[j]+=ww/(1ll<<j),val[j][ji[j]]=v; break; } } for(int o=0;o<=30;++o) for(int i=1;i<=ji[o];++i) for(int j=sum[o];j>=e[o][i];--j) dp[o][j]=max(dp[o][j],dp[o][j-e[o][i]]+val[o][i]); for(int i=1;i<=30;++i){ sum[i]+=(sum[i-1]+1)/2; for(int j=sum[i];j>=0;--j) for(int k=0;k<=j;++k){ dp[i][j]=max(dp[i][j],dp[i][j-k]+dp[i-1][min(sum[i-1],2*k+((w>>i-1)&1))]); } } printf("%d\n",dp[30][0]); } return 0; }