洛谷 1776 宝物筛选 【多重背包+二进制拆分】
【题解】
显然是个多重背包。但直接写背包会超时。所以我们试着优化。
怎么优化?我们发现,每个物品的个数$ai$可以拆分成几个数的和,用这些数中的某几个之和可以表示出$1$到$ai$的所有整数,并且不会超过$ai$
这样我们可以把ai个相同的物品拆分成若干个互相独立的物品,然后跑01背包。
那么如何对$ai$进行拆分?拆分出来的数就是相加起来不超过$ai$的2的$i$次幂以及$ai$减去2的$i$次幂之和的结果。
例如我们可以把19拆分成$1,2,4,8,3$
1 #include<cstdio> 2 #include<algorithm> 3 using namespace std; 4 int n,w,f[200010]; 5 inline int read(){ 6 int f=1,k=0; char c=getchar(); 7 while(c<'0'||c>'9')c=='-'&&(f=-1),c=getchar(); 8 while('0'<=c&&c<='9')k=k*10+c-'0',c=getchar(); 9 return k*f; 10 } 11 int main(){ 12 n=read(); w=read(); 13 while(n--){ 14 int vi=read(),wi=read(),ai=read(),i; 15 for(i=0;((1<<(i+1))-1)<=ai;i++){ 16 for(int j=w;j>=(wi<<i);j--) 17 f[j]=max(f[j-(wi<<i)]+(vi<<i),f[j]); 18 } 19 int tmp=ai-(1<<i)+1; 20 vi=vi*tmp; wi=wi*tmp; 21 for(int j=w;j>=wi;j--) f[j]=max(f[j-wi]+vi,f[j]); 22 } 23 printf("%d\n",f[w]); 24 return 0; 25 }