题意:01背包,但空间巨大。
观察到每个 w_iwi 能写成 a * 2^b (a,b∈N) 的形式,于是可以先把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; }
标签:10,梦幻岛,int,res,maxw,while,宝珠,HNOI2007 From: https://www.cnblogs.com/zimersblog/p/17060789.html