AcWing 6. 多重背包问题 III
有 N 种物品和一个容量是 V 的背包。
第 i 种物品最多有 si 件,每件体积是 vi,价值是 wi。
求解将哪些物品装入背包,可使物品体积总和不超过背包容量,且价值总和最大。
输出最大价值。
输入格式
第一行两个整数,N,V (0<N≤1000, 0<V≤20000),用空格隔开,分别表示物品种数和背包容积。
接下来有 N 行,每行三个整数 vi,wi,si,用空格隔开,分别表示第 i 种物品的体积、价值和数量。
输出格式
输出一个整数,表示最大价值。
数据范围
0<N≤1000
0<V≤20000
0<vi,wi,si≤20000
提示
本题考查多重背包的单调队列优化方法。
输入样例
4 5
1 2 3
2 4 1
3 4 3
4 5 2
输出样例:
10
这道题是一个多重背包问题,上一次我们求多重背包问题的方法是转化成完全背包问题去做,这个方法在这道题不适用,这个时候我们应该介绍一个新的方法。
先说一下完全背包和多重背包的差别:
d
p
[
i
]
[
j
]
=
m
a
x
(
d
p
[
i
−
1
]
[
j
]
,
d
p
[
i
−
1
]
[
j
−
v
]
+
w
,
d
p
[
i
−
1
]
[
j
−
2
∗
v
]
+
2
∗
w
,
.
.
.
,
d
p
[
i
−
1
]
[
j
−
k
∗
v
]
+
k
∗
w
)
dp[i][j] = max(dp[i-1][j], dp[i-1][j-v] + w, dp[i-1][j-2*v] + 2*w,..., dp[i-1][j-k*v] + k*w)
dp[i][j]=max(dp[i−1][j],dp[i−1][j−v]+w,dp[i−1][j−2∗v]+2∗w,...,dp[i−1][j−k∗v]+k∗w)
d
p
[
i
]
[
j
−
v
]
=
m
a
x
(
d
p
[
i
−
1
]
[
j
−
v
]
+
w
,
d
p
[
i
−
1
]
[
j
−
2
∗
v
]
+
2
∗
w
,
.
.
.
,
d
p
[
i
−
1
]
[
j
−
k
∗
v
]
+
k
∗
w
)
dp[i][j-v] = max( dp[i-1][j-v] + w, dp[i-1][j-2*v] + 2*w,..., dp[i-1][j-k*v] + k*w)
dp[i][j−v]=max(dp[i−1][j−v]+w,dp[i−1][j−2∗v]+2∗w,...,dp[i−1][j−k∗v]+k∗w)
根据上面两个式子可以得出,其实完全背包问题是前面 d p [ i ] [ j − v ] dp[i][j-v] dp[i][j−v]前缀 的最大值。
但是多重背包问题就有一点点区别了。
d p [ i ] [ j ] = m a x ( d p [ i − 1 ] [ j ] , d p [ i − 1 ] [ j − v ] + w , dp[i][j] = max(dp[i-1][j], dp[i-1][j-v] + w, dp[i][j]=max(dp[i−1][j],dp[i−1][j−v]+w, d p [ i − 1 ] [ j − 2 ∗ v ] + 2 ∗ w , . . . , d p [ i − 1 ] [ j − s ∗ v ] + s dp[i-1][j-2*v] + 2*w,..., dp[i-1][j-s*v] + s dp[i−1][j−2∗v]+2∗w,...,dp[i−1][j−s∗v]+s ∗ w ) *w) ∗w)
d p [ i ] [ j − v ] = m a x ( d p [ i − 1 ] [ j − v ] , d p [ i − 1 ] [ j − 2 ∗ v dp[i][j-v]=max(dp[i-1][j-v],dp[i-1][j-2*v dp[i][j−v]=max(dp[i−1][j−v],dp[i−1][j−2∗v ] , … … , d p [ i − 1 ] [ j − s ∗ v ] + ( s − 1 ) ∗ v , d p [ i − ],……,dp[i-1][j-s*v]+(s-1)*v,dp[i- ],……,dp[i−1][j−s∗v]+(s−1)∗v,dp[i− 1 ] [ j − ( s + 1 ) ∗ v + s ∗ v ) 1][j-(s+1)*v+s*v) 1][j−(s+1)∗v+s∗v)
看式子可能无法直观 的去看到滑动窗口。
我们手写一下式子。
由此可见,这个是在长度为sv 的滑动窗口,可能我们求的范围小于sv,但是也满足滑动窗口的条件,我们就用滑动窗口的方式去求这个多重背包问题。我们要实现对所有体积进行更新,所以我们开始的起点在 0-v-1 (余)上,这样实现对所有元素的更新。
所以,我们可以得到
dp[j] = dp[j]
dp[j+v] = max(dp[j] + w, dp[j+v])
dp[j+2v] = max(dp[j] + 2w, dp[j+v] + w, dp[j+2v])
dp[j+3v] = max(dp[j] + 3w, dp[j+v] + 2w, dp[j+2v] + w, dp[j+3v])
...
但是,这个队列中前面的数,每次都会增加一个 w ,所以我们需要做一些转换
...
我们在处理的时候会遇到w怎么分配的问题,
y总 给出了很好的解决方案
dp[j] = dp[j]
dp[j+v] = max(dp[j], dp[j+v] - w) + w
dp[j+2v] = max(dp[j], dp[j+v] - w, dp[j+2v] - 2w) + 2w
dp[j+3v] = max(dp[j], dp[j+v] - w, dp[j+2v] - 2w, dp[j+3v] - 3w) + 3w
...
这样,每次入队的值是 dp[j+k*v] - k*w
以上代码参考于链接
最后代码如下:
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=1010,M=20010;
int f[M];
int g[M];
int q[M];
int n,m;
int hh,tt;
int main(void)
{
cin>>n>>m;
for(int i=1;i<=n;i++)
{
int v,w,s;
cin>>v>>w>>s;
memcpy(g,f,sizeof f);
for(int j=0;j<v;j++)
{
hh=0;tt=-1;
for(int k=j;k<=m;k+=v)
{
if(hh<=tt&&q[hh]<k-s*v) hh++;
while(hh<=tt&&g[q[tt]]-(q[tt]-j)/v*w<=g[k]-(k-j)/v*w) tt--;
q[++tt]=k;
f[k]=g[q[hh]]+(k-q[hh])/v*w;
}
}
}
cout<<f[m];
}