多重背包问题:
有n件物品,第i件价值为wi,质量为vi,有c1件,问,给定容量V,求获得的最大价值。
朴素做法:
视为0,1,2,...,k种物品的分组背包 [每组只能选一个]
f[i][j]=Max(f[i][j-k*v[i]]+k*w[i])
但是i,j,k都要枚举,复杂度为 n*V*k
朴素做法的改进:
因为发现用二进制可以表示1..k之内的所有数 [整数二进制打开后为01串,所以可以被二进制表示]
所以将k个物品拆分成1,2,4...2^m,k-2^m ( 其中2^m<=k<2^(m+1) ) 这些物品,然后变成01背包问题。
但是n的数目增多了,复杂度为 n*V*logk
利用单调队列的改进:
1.我们可以发现每个容量都能表示成 v*x+d 的形式[ v表示当前考虑的物品的容量 ]
2.在上一点的启发下,我们发现一个f[v*x+d]在考虑当前物品时,只能由f[v*y+d]转移而来。 [其中x-y<=k]。
也就是说,对v取模的余数相同的容量之间才能互相转移,而且要求x-y<=k。又因为求的是最大值的转移,所以满足单调队列的适用性。
于是乎,我们对于余数d相同的容量分别建一个单调队列,然后枚举x f[x*v+d],进行转移即可。
1 #include<cstdio> 2 #include<cstring> 3 4 inline int in(){ 5 int x=0,flag=1;char ch=getchar(); 6 while(ch!=‘-‘ && (ch>‘9‘ || ch<‘0‘)) ch=getchar(); 7 if(ch==‘-‘) flag=-1,ch=getchar(); 8 while(ch>=‘0‘ && ch<=‘9‘) x=x*10+ch-‘0‘,ch=getchar(); 9 return x*flag; 10 } 11 12 int a[200005],b[200005],f[200005]; 13 int w,v,k,n,V,l,r; 14 15 void insert(int x,int y){ 16 while(l<=r && b[r]<=y) r--; 17 a[++r]=x; b[r]=y; 18 } 19 20 inline int Max(int a,int b){ 21 if(a>b) return a;return b; 22 } 23 24 int main(){ 25 n=in(),V=in(); 26 int Lim; 27 for(int i=1;i<=n;i++){ 28 v=in();w=in();k=in(); 29 if(k==1){ 30 for(int j=V;j>=v;j--) 31 f[j]=Max(f[j],f[j-v]+w); 32 continue; 33 } 34 else if(k<0){ 35 for(int j=v;j<=V;j++) 36 f[j]=Max(f[j],f[j-v]+w); 37 continue; 38 } 39 if(V/v<k) k=V/v; 40 for(int d=0;d<v;d++){ 41 l=1,r=0;Lim=(V-d)/v; 42 for(int x=0;x<=Lim;x++){ 43 insert(x,f[x*v+d]-x*w); 44 if(a[l]<x-k) l++; 45 f[x*v+d]=b[l]+x*w; 46 } 47 } 48 } 49 printf("%d",f[V]); 50 return 0; 51 }
codevs 3269 混合背包
AC通道:http://codevs.cn/problem/3269/
时间: 2024-10-05 04:58:43