[HNOI2007]梦幻岛宝珠

题意:01背包,但空间巨大。

观察到每个 w_iwi 能写成 a * 2^b (a,bN) 的形式,于是可以先把b相同的宝珠做一次合并,随后再进行总的合并。

b相同的宝珠合并时直接可以使用01背包来转移,而总的合并比较复杂,可以采用数位dp的思想用g[i][j]来表示第i位为j,0~i-1位为m&((1<<i)-1)时的最大价值,然后j可以从i-1位进位得到,枚举进位k,转移方程为

g[i][j]=max(g[i][j],f[i][j-k]+g[i-1][min(10*n,2*k+(maxw>>((i-1))&1))]);

注意j上界为10*n

代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll f[31][1010],g[31][1010],cnt=0;
int n,maxw;
int main()
{
    ios::sync_with_stdio(false),cin.tie(0);
    while(cin>>n>>maxw)
    {
        if(n==-1)break;
        memset(f,0,sizeof(f));
        memset(g,0,sizeof(g));
        for(register int i=1,w,v;i<=n;++i)
        {
            cin>>w>>v;
            int res=0;
            while(!(w&1))w>>=1,res++;
            for(int j=10*n;j>=w;--j)f[res][j]=max(f[res][j],f[res][j-w]+v);
        }
        cnt=0;
        while(maxw>=1<<cnt+1)cnt++;
        memcpy(g[0],f[0],sizeof(g[0]));
        for(register int i=1;i<=cnt;++i)
            for(register int j=0;j<=10*n;++j)
                for(register int k=0;k<=j;++k)
                g[i][j]=max(g[i][j],f[i][j-k]+g[i-1][min(10*n,2*k+(maxw>>((i-1))&1))]);
        cout<<g[cnt][1]<<endl;
    }
    return 0;
}

 

posted @ 2023-01-18 22:34  zhangminrui  阅读(23)  评论(0)    收藏  举报