P3188 [HNOI2007] 梦幻岛宝珠

Captain_Paul

2018-07-11 14:58:39

Personal

当01背包的体积范围达到2^30时,应该怎么办? 题目中已经注明每一个物品的体积都满足$ 2^a*b $ 所以根据$ 2^a $,可以把所有物品分组 令$ f[ i ][ j ] $表示体积为$ 2^i*j $ 时的最大价值 对每一组分别进行01背包即可求出 再改变一下f数组的含义,用已经得到的答案进行组间dp $ f[ i ][ j ] $表示体积为$ 2^i*j+(m$&$(1<<i)-1) $时的最大价值 状态转移方程为 $ chmax(f[i][j],f[i][k]+f[i-1][(j-k<<1)+(m>>i-1)$&$ 1]) $ ```cpp #include<cstdio> #include<cstring> #include<cctype> #include<algorithm> #define reg register using namespace std; int n,m,ans,f[31][1005];//j*2^i inline int read() { int x=0,w=1; char c=getchar(); while (!isdigit(c)&&c!='-') c=getchar(); if (c=='-') c=getchar(),w=-1; while (isdigit(c)) { x=(x<<1)+(x<<3)+c-'0'; c=getchar(); } return x*w; } int main() { while (1) { n=read(),m=read(); if (n<0) break; memset(f,0,sizeof(f)); ans=0; for (reg int i=1;i<=n;i++) { int w=read(),v=read(),k=0; while (!(w&1)) ++k,w>>=1; for (reg int j=min(m>>k,1000);j>=w;j--) { f[k][j]=max(f[k][j],f[k][j-w]+v); ans=max(ans,f[k][j]); } } for (reg int i=1;i<31&&(1<<i)<m;i++) for (reg int j=min(m>>i,1000);~j;j--) { for (reg int k=j;~k;k--) f[i][j]=max(f[i][j],f[i][k]+f[i-1][min((j-k<<1)+((m>>i-1)&1),1000)]); ans=max(ans,f[i][j]); } printf("%d\n",ans); } return 0; } ```