problem
有一个 m m m 体积的背包。有 n n n 物品,物品有体积 v i v_i vi,价值 w i w_i wi 和数目 c i c_i ci,求能获得的最大价值。
数据范围: 1 ≤ n ≤ 100 1 \le n \le 100 1≤n≤100, 1 ≤ m ≤ 5 × 1 0 4 1 \le m \le 5\times10^4 1≤m≤5×104, 1 ≤ c i ≤ 200 1\le c_i\le200 1≤ci≤200。
solution
今天 L 一说,我才发现不会单调队列优化多重背包。
学一学吧。
考虑普通的背包的转移,有:
f [ i ] = max k = 0 min ( c i , ⌊ i v i ⌋ ) { f [ i − k v i ] + k w i } f[i]=\max_{k=0}^{\min(c_i,\lfloor\frac{i}{v_i}\rfloor)}\{f[i-kv_i]+kw_i\} f[i]=k=0maxmin(ci,⌊vii⌋){f[i−kvi]+kwi}
然后发现,只有在 % v \%v %v 意义下相同的才能转移,那么可以对于每个余数做 d p dp dp。
那么( d d d 是余数):
f [ i × v + d ] = max k { f [ k × v + d ] + ( i − k ) × w } f [ i × v + d ] = max k { f [ k × v + d ] − k w } + i w \begin{aligned} f[i\times v+d]&=\max_k\{f[k\times v+d]+(i-k)\times w\}\\ f[i\times v+d]&=\max_k\{f[k\times v+d]-kw\}+iw \end{aligned} f[i×v+d]f[i×v+d]=kmax{f[k×v+d]+(i−k)×w}=kmax{f[k×v+d]−kw}+iw
可以用单调队列来维护 f [ k × v + d ] − k w f[k\times v+d]-kw f[k×v+d]−kw 的最大值。
时间复杂度 O ( n m ) O(nm) O(nm)。
code
#include<bits/stdc++.h>
using namespace std;
namespace IO{
const int Rlen=1<<22|1;
char buf[Rlen],*p1,*p2;
inline char gc(){
return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,Rlen,stdin),p1==p2)?EOF:*p1++;
}
template<typename T>
inline T Read(){
char c=gc();T x=0,f=1;
while(!isdigit(c)) f^=(c=='-'),c=gc();
while( isdigit(c)) x=(x+(x<<2)<<1)+(c^48),c=gc();
return f?x:-x;
}
inline int in() {return Read<int>();}
}
using IO::in;
const int N=5e4+5;
int n,m;
int Q[N],f[N],num[N];
int main(){
n=in(),m=in();
for(int i=1;i<=n;++i){
int v=in(),w=in(),c=in();
for(int d=0;d<v;++d){
int L=1,R=0;
for(int j=0;j<=(m-d)/v;++j){
int tmp=f[j*v+d]-j*w;
while(L<=R&&Q[R]<=tmp) R--;
Q[++R]=tmp,num[R]=j;
while(L<R&&j-num[L]>c) L++;
f[j*v+d]=max(f[j*v+d],Q[L]+j*w);
}
}
}
printf("%d\n",f[m]);
return 0;
}
本文介绍了一种使用单调队列优化多重背包问题的算法。通过将问题分解为针对每个余数的子问题,利用单调队列维护最大价值状态,实现了O(nm)的时间复杂度。代码示例展示了如何在限制条件下计算最大价值。
1万+

被折叠的 条评论
为什么被折叠?



