BZOJ1190: [HNOI2007]梦幻岛宝珠
【传送门:BZOJ1190】
简要题意:
给出n个物品以及背包的总容量W,每个物品有一个价值v和体积w,保证w=a*2b(a<=10且b<=30)
求出最大价值
题解:
直接01背包显然爆炸
实际上我们可以将b相同的物品分成一组,f[i][j]表示b=i的所有物品,组成j*2b为总体积所能得到的最大价值
因为我们可以确定j<=10*n=1000,所以可以先分开各自01背包
然后将得到的f数组再组合,这时f数组就是f[i][j]表示2^0到2^i组内,容量为j*2^i+(w&((1<<i)-1))时的最大价值
因为后面的物品的总容量最多达到10*n,所以转移的时候对10*m取一下min就行了
参考代码:
#include<cstdio> #include<cstring> #include<cstdlib> #include<cmath> #include<algorithm> using namespace std; int f[41][1100]; int main() { int n,W,i,j,k,w,v,len,ww; while(1) { scanf("%d%d",&n,&W); if(n==-1&&W==-1) break; memset(f,0,sizeof(f)); for(i=1;i<=n;++i) { scanf("%d%d",&w,&v); k=0; while(w%2==0) w=w/2,k++; for(j=10*n;j>=w;--j) f[k][j]=max(f[k][j-w]+v,f[k][j]); } len=0,ww=W; while(ww!=0) ww/=2,len++; len--; for(i=1;i<=len;++i) { for(j=10*n;j>=0;--j) { for(k=0;k<=j;++k) { f[i][j]=max(f[i][j],f[i][j-k]+f[i-1][min(10*n,(k*2)+((W>>(i-1))&1))]); } } } printf("%d\n",f[len][1]); } return 0; }
渺渺时空,茫茫人海,与君相遇,幸甚幸甚