多重背包裸题…
c c 表示物品个数, 表示物品体积, v v 表示物品价格,以下不做特殊解释
多重背包的问题,我们可以物品拆开,然后用01背包来求,复杂度是
但显然这样复杂度比较大,有两种优化:二进制分组、单调队列优化
先来说下二进制分组,也是把物品拆开,只不过不是拆成一个,而是根据二进制拆,因为我们知道1、2、4、8…可以组成所有数。因此把一个物品拆成log份,复杂度为 O(m∑logc) O ( m ∑ l o g c )
而单调队列优化可以优化到
O(nm)
O
(
n
m
)
考虑转移方程
f[i][j]=max(f[i−1][j−k∗w[i]]+k∗v[i])
f
[
i
]
[
j
]
=
m
a
x
(
f
[
i
−
1
]
[
j
−
k
∗
w
[
i
]
]
+
k
∗
v
[
i
]
)
其中
0<=k<=min(c[i],jw[i])
0
<=
k
<=
m
i
n
(
c
[
i
]
,
j
w
[
i
]
)
设
a=j/w[i],b=j
a
=
j
/
w
[
i
]
,
b
=
j
,即
j=a∗w[i]+b
j
=
a
∗
w
[
i
]
+
b
转移方程变为
f[i][w∗v[i]+b]=max(f[i−1][(a−k)∗w[i]+b]+k∗v[i])
f
[
i
]
[
w
∗
v
[
i
]
+
b
]
=
m
a
x
(
f
[
i
−
1
]
[
(
a
−
k
)
∗
w
[
i
]
+
b
]
+
k
∗
v
[
i
]
)
其中
0<=k<=min(a,c[i])
0
<=
k
<=
m
i
n
(
a
,
c
[
i
]
)
再令
s=a−k
s
=
a
−
k
,得到
f[i][a∗w[i]+b]=max(f[i−1][s∗w[i]+b]−s∗v[i])+a∗v[i]
f
[
i
]
[
a
∗
w
[
i
]
+
b
]
=
m
a
x
(
f
[
i
−
1
]
[
s
∗
w
[
i
]
+
b
]
−
s
∗
v
[
i
]
)
+
a
∗
v
[
i
]
其中
a−min(a,c[i])<=s<=a
a
−
m
i
n
(
a
,
c
[
i
]
)
<=
s
<=
a
因此枚举
b
b
,用单调队列维护 最大值即可
#include <cstdio>
#include <algorithm>
using namespace std;
#define N 10010
#define mp make_pair
pair<int,int>q[N];
int n,m,w[N],v[N],c[N],f[N*5];
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++) scanf("%d%d%d",&w[i],&v[i],&c[i]);
for(int i=1;i<=n;i++){
c[i]=min(c[i],m/w[i]);
for(int d=0;d<w[i];d++){ //枚举余数
int st=1,ed=0;
for(int j=0;j<=(m-d)/w[i];j++){
int tmp=f[j*w[i]+d]-v[i]*j;
while(st<=ed && tmp>=q[ed].first) ed--;
while(st<=ed && q[st].second<j-min(c[i],j)) st++;
q[++ed]=mp(tmp,j);
f[j*w[i]+d]=q[st].first+j*v[i];
}
}
}
printf("%d\n",f[m]);
return 0;
}