[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;
}

 

posted @ 2018-10-19 19:30  comld  阅读(125)  评论(0编辑  收藏  举报