BZOJ1190_梦幻岛宝珠_KEY

题目传送门

观察数据a*2^b,转化成二进制后,后面跟了b位的0,可以转化为一个分层背包。

先预处理出每个物品是哪一层的,并放在同层内DP。

同层内直接背包,考虑层与层之间的DP。

第一维枚举层数,然后做类似于背包的DP,细节看code。

code:

/**************************************************************
    Problem: 1190
    User: yekehe
    Language: C++
    Result: Accepted
    Time:5932 ms
    Memory:960 kb
****************************************************************/
 
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
 
int read()
{
    char c;while(c=getchar(),c!='-'&&(c<'0'||c>'9'));
    int x=0,y=1;c=='-'?y=-1:x=c-'0';
    while(c=getchar(),c>='0'&&c<='9')x=x*10+c-'0';
    return x*y;
}
 
int w[105],v[105],f[35][1005];
 
void work(int i)//预处理
{
    for(int k=0;k<=30;k++)
        if(w[i]&(1<<k)){
            for(int j=1000;j>=(w[i]>>k);j--)
                f[k][j]=max(f[k][j-(w[i]>>k)]+v[i],f[k][j]);
            return ;
        }
}
 
int main()
{
    while(1){
        int N=read(),M=read(),ans=0;
        if(N<0&&M<0)break;
        memset(f,0,sizeof f);
            for(int i=1;i<=N;i++)
                w[i]=read(),v[i]=read();
            for(int i=1;i<=N;i++)work(i);
            for(int i=0;i<=30;i++)
                for(int j=1;j<=1000;j++)
                    f[i][j]=max(f[i][j],f[i][j-1]);
            for(int i=1;i<=30&&1<<i<=M;i++){//枚举层数
                for(int j=min(1000,M>>i);j>=0;j--){//枚举背包容量,类似01背包转移
                    for(int k=0;k<=j;k++){
                        f[i][j]=max(f[i][j],f[i-1][min((k<<1)+(M>>i-1&1),1000)]+f[i][j-k]);//k*2是因为从上一层转移。
                        ans=max(ans,f[i][j]);
                    }
                }
            }
        printf("%d\n",ans);
    }
    return 0;
}

 

posted @ 2018-04-14 15:48  Cptraser  阅读(143)  评论(0编辑  收藏  举报