多重背包问题
状态表示:
f
[
i
]
[
j
]
f[i][j]
f[i][j]: 在前
i
i
i 个数中选, 每个物品不超过
s
i
s_i
si 个体积总和不超过
j
j
j 的集合
状态属性: 总价值的最大值
状态计算
1.第
i
i
i个物品选
0
0
0 个
f
[
i
]
[
j
]
=
m
a
x
(
f
[
i
]
[
j
]
,
f
[
i
−
1
]
[
j
−
0
∗
v
[
i
]
]
+
0
∗
w
[
i
]
)
;
f[i][j]= max(f[i][j], f[i - 1][j - 0 * v[i]] + 0 * w[i]);
f[i][j]=max(f[i][j],f[i−1][j−0∗v[i]]+0∗w[i]);
2.第
i
i
i 个物品选
1
1
1个
f
[
i
]
[
j
]
=
m
a
x
(
f
[
i
]
[
j
]
,
f
[
i
−
1
]
[
j
−
1
∗
v
[
i
]
]
+
1
∗
w
[
i
]
)
f[i][j] = max(f[i][j], f[i - 1][j - 1 * v[i]] + 1 * w[i])
f[i][j]=max(f[i][j],f[i−1][j−1∗v[i]]+1∗w[i]);
3.第
i
i
i 个物品选
2
2
2 个
f
[
i
]
[
j
]
=
m
a
x
(
f
[
i
]
[
j
]
,
f
[
i
−
1
]
[
j
−
3
∗
v
[
i
]
]
+
2
∗
w
[
i
]
)
;
f[i][j] = max(f[i][j], f[i - 1][j - 3 * v[i]] + 2 * w[i]);
f[i][j]=max(f[i][j],f[i−1][j−3∗v[i]]+2∗w[i]);
4.第
i
i
i 个物品选
3
3
3 个
f
[
i
]
[
j
]
=
m
a
x
(
f
[
i
]
[
j
]
,
f
[
i
−
1
]
[
j
−
4
∗
v
[
i
]
]
+
4
∗
w
[
i
]
)
;
f[i][j] = max(f[i][j], f[i - 1][j - 4 * v[i]] + 4 * w[i]);
f[i][j]=max(f[i][j],f[i−1][j−4∗v[i]]+4∗w[i]);
.
.
.
...
...
第i个物品选 s s s 个 f [ i ] [ j ] = m a x ( f [ i ] [ j ] , f [ i − 1 ] [ j − s [ i ] ∗ v [ i ] ] + s [ i ] ∗ w [ i ] ) ; f[i][j] = max(f[i][j], f[i - 1][j - s[i] * v[i]] + s[i] * w[i]); f[i][j]=max(f[i][j],f[i−1][j−s[i]∗v[i]]+s[i]∗w[i]);
1.朴素做法 时间复杂度 O(n × m × s)
for(int i = 1; i <= n; i ++ )
for(int j = 0; j <= m; j ++ )
for(int k = 0; k <= s[i] && j >= k * v[i]; k ++ )
f[i][j] = max(f[i][j], f[i - 1][j - k * v[i]] + k * w[i]);
2.一些奇奇怪怪的做法 时间复杂度: 比朴素做法还慢
将
s
i
s_i
si 个物品拆分成一个个物品 当做
01
01
01 背包来做
int cnt = n;
for(int i = 1; i <= n; i ++ )
{
cin >> v[i] >> w[i] >> s[i];
for(int j = 1; j < s[i]; j ++ )
v[ ++ cnt] = v[i], w[cnt] = w[i];
}
for(int i = 1; i <= cnt; i ++ )
for(int j = 0; j <= m; j ++ )
{
f[i][j] = f[i - 1][j];
if(j >= v[i])
f[i][j] = max(f[i][j], f[i - 1][j - v[i]] + w[i]);
}
3.二进制优化 时间复杂度 O(
n
2
l
o
g
s
n^2logs
n2logs)
如果直接遍历转化为
01
01
01 背包问题,是每次都拿一个来问,取了好还是不取好。那么根据数据范围,这样的时间复杂度是O(
n
3
n^3
n3),这个时间复杂度很不理想,我们肯定想去优化它,我们不难知道任何一个整数都能用一个二进制数来表示,所以我们可以将s个物品拆分成多个二进制数, 如
9
=
2
0
+
2
3
9 = 2^0 + 2^3
9=20+23,
9
9
9 个物品可以拆分为体积为
8
v
8v
8v,价值为
8
w
8w
8w 的物品和体积为
v
v
v,价值为
w
w
w 的物品,这样可以大大降低时间复杂度
for(int i = 1; i <= n; i ++ )
{
int V, W, S;
cin >> V >> W >> S;
int k = 1;
while(k <= S)
{
v[++ cnt] = k * V;
w[cnt] = k * W;
S -= k;
k <<= 1;
}
if(S > 0 ) v[++ cnt] = S * V, w[cnt] = S * W;
}
n = cnt;
for(int i = 1; i <= n; i ++ )
for(int j = 0; j <= m; j ++ )
{
f[i][j] = f[i - 1][j];
if(j >= v[i])
f[i][j] = max(f[i][j], f[i - 1][j - v[i]] + w[i]);
}
代码看起来很合理,时间复杂度也很满意,但是当你提交时会发现SE了, 为啥? 答案是f数组开两维需要的空间很大,容易爆内存,所以我们需要进行空间优化
3.空间优化
通过完全背包和
01
01
01 背包的空间优化,我们肯定也想对多重背包进行空间优化
01
01
01背包
f
[
i
]
[
j
]
=
m
a
x
(
f
[
i
]
[
j
]
,
f
[
i
−
1
]
[
j
−
v
[
i
]
]
+
w
[
i
]
)
;
f[i][j] = max(f[i][j], f[i - 1][j - v[i]] + w[i]);
f[i][j]=max(f[i][j],f[i−1][j−v[i]]+w[i]);
完全背包
f
[
i
]
[
j
]
=
m
a
x
(
f
[
i
]
[
j
]
,
f
[
i
−
1
]
[
j
−
v
[
i
]
]
+
w
[
i
]
)
;
f[i][j] = max(f[i][j], f[i - 1][j - v[i]] + w[i]);
f[i][j]=max(f[i][j],f[i−1][j−v[i]]+w[i]);
我们会发现惊人的相似,不,就是完全一样,那我们自然可以想到从大到小枚举体积,然后将空间压缩为一维
for(int i = 1; i <= n; i ++ )
{
int V, W, S;
cin >> V >> W >> S;
int k = 1;
while(k <= S)
{
v[++ cnt] = k * V;
w[cnt] = k * W;
S -= k;
k <<= 1;
}
if(S > 0 ) v[++ cnt] = S * V, w[cnt] = S * W;
}
n = cnt;
for(int i = 1; i <= n; i ++ )
for(int j = m; j >= v[i]; j -- )
f[j] = max(f[j], f[j - v[i]] + w[i]);
但是我们这时候会想,为啥我们不能像完全背包哪一样优化呢,我们可以试着模仿完全背包做一下
我们会发现下面比上面多了一项,但是完全背包为啥项数是相同的呢? 因为完全背包是可以取无穷次的,所以项数一定是相同的,因为多重背包上下项数不同,所以我们不能进行优化
4.进行了上述的空间时间优化后,我们还是不够满意 我们尝试能不能将时间复杂度降低到O( N × V N × V N×V) 呢? 答案是可以的,那要怎么优化呢? 我们将会用到一个数据结构, 叫单调队列
f
[
i
]
[
j
]
=
m
a
x
(
f
[
i
−
1
]
[
j
]
,
f
[
i
−
1
]
[
j
−
v
]
+
w
,
⋯
,
f
[
i
−
1
]
[
j
−
s
∗
v
]
+
s
∗
w
)
;
f[i][j] = max(f[i − 1][j],f[i − 1][j − v] + w,⋯,f[i − 1][j − s * v] + s * w);
f[i][j]=max(f[i−1][j],f[i−1][j−v]+w,⋯,f[i−1][j−s∗v]+s∗w);
f
[
i
]
[
j
−
v
]
=
m
a
x
(
f
[
i
−
1
]
[
j
−
v
]
,
.
.
.
f
[
i
−
1
]
[
j
−
(
s
+
1
)
∗
v
]
+
s
∗
w
)
;
f[i][j − v] = max( f[i − 1][j − v], ... f[i − 1][j − (s + 1) * v] + s * w);
f[i][j−v]=max(f[i−1][j−v],...f[i−1][j−(s+1)∗v]+s∗w);
f
[
i
]
[
j
−
2
∗
v
]
=
m
a
x
(
f
[
i
−
1
]
[
j
−
2
∗
v
]
,
f
[
i
−
1
]
[
j
−
3
∗
v
]
+
w
,
⋯
,
f
[
i
−
1
]
[
j
−
(
s
+
2
)
∗
v
)
+
s
∗
w
]
f[i][j− 2 * v] = max(f[i − 1][j − 2 * v], f[i − 1][j − 3 * v] + w,⋯,f[i − 1][j − (s + 2) * v) + s * w]
f[i][j−2∗v]=max(f[i−1][j−2∗v],f[i−1][j−3∗v]+w,⋯,f[i−1][j−(s+2)∗v)+s∗w]
.
.
.
...
...
f
[
i
]
[
r
+
s
∗
v
]
=
m
a
x
(
f
[
i
−
1
]
[
r
+
s
∗
v
]
,
f
[
i
−
1
]
[
r
+
(
s
−
1
)
∗
v
)
]
+
w
,
.
.
.
,
f
[
i
−
1
]
[
r
]
)
+
s
w
)
;
f[i][r + s * v]= max(f[i − 1][r + s * v],f[i − 1][r + (s − 1) * v)] + w,...,f[i − 1][r])+sw);
f[i][r+s∗v]=max(f[i−1][r+s∗v],f[i−1][r+(s−1)∗v)]+w,...,f[i−1][r])+sw);
f
[
i
]
[
r
+
(
s
−
1
)
∗
v
]
=
m
a
x
(
f
[
i
−
1
]
[
r
+
(
s
−
1
)
∗
v
]
,
.
.
.
,
f
[
i
−
1
]
[
r
]
+
(
s
−
1
)
∗
w
)
f[i][r + (s − 1) * v] = max(f[i−1][r + (s − 1) * v],...,f[i − 1][r]+ (s − 1) * w)
f[i][r+(s−1)∗v]=max(f[i−1][r+(s−1)∗v],...,f[i−1][r]+(s−1)∗w)
f
[
i
]
[
r
+
2
∗
v
]
=
m
a
x
(
f
[
i
−
1
]
[
r
+
2
∗
v
]
,
f
[
i
−
1
]
[
r
+
v
]
+
w
,
f
[
i
−
1
]
[
r
]
)
+
2
∗
w
)
f[i][r + 2 * v] = max(f[i − 1][r + 2 * v],f[i − 1][r + v] + w, f[i − 1][r]) + 2 * w)
f[i][r+2∗v]=max(f[i−1][r+2∗v],f[i−1][r+v]+w,f[i−1][r])+2∗w)
f
[
i
]
[
r
+
v
]
=
m
a
x
(
f
[
i
−
1
]
[
r
+
v
]
,
f
[
i
−
1
]
[
r
]
+
w
)
f[i][r + v] = max(f[i − 1][r + v],f[i − 1][r] + w)
f[i][r+v]=max(f[i−1][r+v],f[i−1][r]+w)
f
[
i
]
[
r
]
=
f
[
i
−
1
]
[
r
]
f[i][r] = f[i − 1][r]
f[i][r]=f[i−1][r]
为了更好的阅读体验我们可以先把
i
i
i 去掉
f
[
j
]
=
m
a
x
(
f
[
j
]
,
f
[
j
−
v
]
+
w
,
⋯
,
f
[
j
−
s
∗
v
]
+
s
∗
w
)
;
f[j] = max(f[j],f[j − v] + w,⋯,f[j − s * v] + s * w);
f[j]=max(f[j],f[j−v]+w,⋯,f[j−s∗v]+s∗w);
f
[
j
−
v
]
=
m
a
x
(
f
[
j
−
v
]
,
.
.
.
f
[
j
−
(
s
+
1
)
∗
v
]
+
s
∗
w
)
;
f[j − v] = max( f[j − v], ... f[j − (s + 1) * v] + s * w);
f[j−v]=max(f[j−v],...f[j−(s+1)∗v]+s∗w);
f
[
j
−
2
∗
v
]
=
m
a
x
(
f
[
j
−
2
∗
v
]
,
f
[
j
−
3
∗
v
]
+
w
,
⋯
,
f
[
j
−
(
s
+
2
)
∗
v
)
+
s
∗
w
]
f[j− 2 * v] = max(f[j − 2 * v], f[j − 3 * v] + w,⋯,f[j − (s + 2) * v) + s * w]
f[j−2∗v]=max(f[j−2∗v],f[j−3∗v]+w,⋯,f[j−(s+2)∗v)+s∗w]
.
.
.
...
...
f
[
r
+
s
∗
v
]
=
m
a
x
(
f
[
r
+
s
∗
v
]
,
f
[
r
+
(
s
−
1
)
∗
v
)
]
+
w
,
.
.
.
,
f
[
r
]
)
+
s
w
)
;
f[r + s * v]= max(f[r + s * v],f[r + (s − 1) * v)] + w,...,f[r])+sw);
f[r+s∗v]=max(f[r+s∗v],f[r+(s−1)∗v)]+w,...,f[r])+sw);
f
[
r
+
(
s
−
1
)
∗
v
]
=
m
a
x
(
f
[
r
+
(
s
−
1
)
∗
v
]
,
.
.
.
,
f
[
r
]
+
(
s
−
1
)
∗
w
)
f[r + (s − 1) * v] = max(f[r + (s − 1) * v],...,f[r]+ (s − 1) * w)
f[r+(s−1)∗v]=max(f[r+(s−1)∗v],...,f[r]+(s−1)∗w)
f
[
r
+
2
∗
v
]
=
m
a
x
(
f
[
r
+
2
∗
v
]
,
f
[
r
+
v
]
+
w
,
f
[
r
]
)
+
2
∗
w
)
f[r + 2 * v] = max(f[r + 2 * v],f[r + v] + w, f[r]) + 2 * w)
f[r+2∗v]=max(f[r+2∗v],f[r+v]+w,f[r])+2∗w)
f
[
r
+
v
]
=
m
a
x
(
f
[
r
+
v
]
,
f
[
r
]
+
w
)
f[r + v] = max(f[r + v],f[r] + w)
f[r+v]=max(f[r+v],f[r]+w)
f
[
r
]
=
f
[
r
]
f[r] = f[r]
f[r]=f[r]
看到这里你可能会疑惑了, 这是啥啊, 我咋看不懂, r是啥,为啥前面是减去v,后面咋又加上
v
v
v 了
在这里,
r
r
r 是指的
j
j
j 除以
v
v
v 后的余数,因为用一个物品会消耗v,用s个物品会消耗
s
×
v
s × v
s×v 的体积, 最后会剩余一部分体积,即
j
j % v
j, 所以
f
[
i
]
[
j
−
v
]
f[i][j - v]
f[i][j−v] 最后就会变成
f
[
i
]
[
r
]
f[i][r]
f[i][r]
然后往回倒退,每次就是加上
v
v
v了
然后再把这个递推式倒过来观察
f
[
i
]
[
r
]
=
f
[
i
−
1
]
[
r
]
f[i][r] = f[i − 1][r]
f[i][r]=f[i−1][r]
f
[
i
]
[
r
+
v
]
=
m
a
x
(
f
[
i
−
1
]
[
r
+
v
]
,
f
[
i
−
1
]
[
r
]
+
w
)
f[i][r + v] = max(f[i − 1][r + v],f[i − 1][r] + w)
f[i][r+v]=max(f[i−1][r+v],f[i−1][r]+w)
f
[
i
]
[
r
+
2
∗
v
]
=
m
a
x
(
f
[
i
−
1
]
[
r
+
2
∗
v
]
,
f
[
i
−
1
]
[
r
+
v
]
+
w
,
f
[
i
−
1
]
[
r
]
)
+
2
∗
w
)
f[i][r + 2 * v] = max(f[i − 1][r + 2 * v],f[i − 1][r + v] + w, f[i − 1][r]) + 2 * w)
f[i][r+2∗v]=max(f[i−1][r+2∗v],f[i−1][r+v]+w,f[i−1][r])+2∗w)
.
.
.
...
...
f
[
i
]
[
r
+
(
s
−
1
)
∗
v
]
=
m
a
x
(
f
[
i
−
1
]
[
r
+
(
s
−
1
)
∗
v
]
,
.
.
.
,
f
[
i
−
1
]
[
r
]
+
(
s
−
1
)
∗
w
)
f[i][r + (s − 1) * v] = max(f[i − 1][r + (s − 1) * v],...,f[i − 1][r] + (s − 1) * w)
f[i][r+(s−1)∗v]=max(f[i−1][r+(s−1)∗v],...,f[i−1][r]+(s−1)∗w)
f
[
i
]
[
r
+
s
∗
v
]
=
m
a
x
(
f
[
i
−
1
]
[
r
+
s
∗
v
]
,
f
[
i
−
1
]
[
r
+
(
s
−
1
)
∗
v
)
]
+
w
,
.
.
.
,
f
[
i
−
1
]
[
r
]
)
+
s
∗
w
)
;
f[i][r + s * v] = max( f[i − 1][r + s * v],f[i − 1][r + (s − 1) * v)] + w,...,f[i − 1][r]) + s * w);
f[i][r+s∗v]=max(f[i−1][r+s∗v],f[i−1][r+(s−1)∗v)]+w,...,f[i−1][r])+s∗w); 滑动窗口已满
f
[
i
]
[
r
+
(
s
+
1
)
∗
v
]
=
m
a
x
(
f
[
i
−
1
]
[
r
+
(
s
+
1
)
∗
v
]
,
f
[
i
−
1
]
[
r
+
s
∗
v
]
+
w
,
⋯
,
f
[
i
−
1
]
[
r
+
v
]
+
s
∗
w
)
;
f[i][r + (s + 1) * v] = max(f[i - 1][r + (s + 1) * v], f[i - 1][r + s * v] + w,⋯,f[i - 1][r + v]+ s * w);
f[i][r+(s+1)∗v]=max(f[i−1][r+(s+1)∗v],f[i−1][r+s∗v]+w,⋯,f[i−1][r+v]+s∗w); 滑动窗口已满
. . . ... ...
f
[
i
]
[
j
−
2
∗
v
]
=
m
a
x
(
f
[
i
−
1
]
[
j
−
2
∗
v
]
,
f
[
i
−
1
]
[
j
−
3
∗
v
]
+
w
,
⋯
,
f
[
i
−
1
]
[
j
−
(
s
+
2
)
∗
v
)
+
s
∗
w
]
;
f[i][j− 2 * v] = max(f[i − 1][j − 2 * v], f[i − 1][j − 3 * v] + w,⋯,f[i − 1][j − (s + 2) * v) + s * w];
f[i][j−2∗v]=max(f[i−1][j−2∗v],f[i−1][j−3∗v]+w,⋯,f[i−1][j−(s+2)∗v)+s∗w]; 滑动窗口已满
f
[
i
]
[
j
−
v
]
=
m
a
x
(
f
[
i
−
1
]
[
j
−
v
]
,
.
.
.
f
[
i
−
1
]
[
j
−
(
s
+
1
)
∗
v
]
+
s
∗
w
)
;
f[i][j − v] = max(f[i − 1][j − v], ... f[i − 1][j − (s + 1) * v] + s * w);
f[i][j−v]=max(f[i−1][j−v],...f[i−1][j−(s+1)∗v]+s∗w); 滑动窗口已满
f
[
i
]
[
j
]
=
m
a
x
(
f
[
i
−
1
]
[
j
]
,
f
[
i
−
1
]
[
j
−
v
]
+
w
,
⋯
,
f
[
i
−
1
]
[
j
−
s
∗
v
]
+
s
∗
w
)
;
f[i][j] = max(f[i − 1][j],f[i − 1][j − v] + w,⋯,f[i − 1][j − s * v] + s * w);
f[i][j]=max(f[i−1][j],f[i−1][j−v]+w,⋯,f[i−1][j−s∗v]+s∗w); 滑动窗口已满
或许你还没有看懂,那么你可以看这里
f
r
f_r
fr
f
r
+
2
v
,
f
r
+
v
,
f
r
f_{r + 2v},f_{r + v}, f_r
fr+2v,fr+v,fr
f
r
+
(
s
−
1
)
v
,
f
r
+
(
s
−
2
)
v
,
.
.
.
f
r
+
2
v
,
f
r
+
v
,
f
r
f_{r + (s - 1)v}, f_{r + (s - 2)v}, ... f_{r + 2v}, f_{r + v}, f_r
fr+(s−1)v,fr+(s−2)v,...fr+2v,fr+v,fr
f
r
+
s
v
,
f
r
+
(
s
−
1
)
v
,
.
.
.
f
r
+
2
v
,
f
r
+
v
,
f
r
f_{r + sv}, f_{r + (s - 1)v}, ... f_{r + 2v}, f_{r + v}, f_r
fr+sv,fr+(s−1)v,...fr+2v,fr+v,fr
. . . ... ...
f
j
−
2
v
,
f
j
−
3
v
,
f
j
−
4
v
,
.
.
.
f
j
−
(
s
+
2
)
v
f_{j - 2v}, f_{j - 3v}, f_{j - 4v}, ... f_{j - (s + 2)v}
fj−2v,fj−3v,fj−4v,...fj−(s+2)v
f
j
−
v
,
f
j
−
2
v
,
f
j
−
3
v
,
.
.
.
f
j
−
(
s
+
1
)
v
f_{j - v}, f_{j - 2v}, f_{j - 3v}, ... f_{j - (s + 1)v}
fj−v,fj−2v,fj−3v,...fj−(s+1)v
f
j
,
f
j
−
v
,
f
j
−
2
v
,
.
.
.
f
j
−
s
v
f_j, f_{j - v}, f_{j - 2v},... f_{j - sv}
fj,fj−v,fj−2v,...fj−sv
这里差不多就能看出规律了,然后我们再把 w w w 加上去,
f
r
f_r
fr
f
r
+
2
v
,
f
r
+
v
+
w
,
f
r
+
2
w
f_{r + 2v}, f_{r + v} + w, f_r + 2w
fr+2v,fr+v+w,fr+2w
f
r
+
(
s
−
1
)
v
,
f
r
+
(
s
−
2
)
v
+
w
,
.
.
.
f
r
+
2
v
+
(
s
−
3
)
w
,
f
r
+
v
+
(
s
−
2
)
w
,
f
r
+
(
s
−
1
)
w
f_{r + (s - 1)v}, f_{r + (s - 2)v} + w, ... f_{r + 2v} + (s - 3)w, f_{r + v} + (s - 2)w, f_r + (s - 1)w
fr+(s−1)v,fr+(s−2)v+w,...fr+2v+(s−3)w,fr+v+(s−2)w,fr+(s−1)w
f
r
+
s
v
,
f
r
+
(
s
−
1
)
v
+
w
,
.
.
.
f
r
+
2
v
+
(
s
−
2
)
w
,
f
r
+
v
+
(
s
−
1
)
w
,
f
r
+
s
w
f_{r + sv}, f_{r + (s - 1)v} + w, ... f_{r + 2v} + (s - 2)w, f_{r + v} + (s - 1)w, f_r + sw
fr+sv,fr+(s−1)v+w,...fr+2v+(s−2)w,fr+v+(s−1)w,fr+sw
然后我们要将 v , w v,w v,w 建立起关系,我们提取 w w w
f
r
f_r
fr
f
r
+
2
v
−
2
w
,
f
r
+
v
−
w
,
f
r
f_{r + 2v} - 2w, f_{r + v} -w, f_r
fr+2v−2w,fr+v−w,fr
f
r
+
(
s
−
1
)
v
−
(
s
−
1
)
w
,
f
r
+
(
s
−
2
)
v
+
(
s
−
2
)
w
,
.
.
.
f
r
+
2
v
−
2
w
,
f
r
+
v
−
w
,
f
r
f_{r + (s - 1)v} - (s - 1)w, f_{r + (s - 2)v} + (s - 2)w, ... f_{r + 2v} - 2w, f_{r + v} - w, f_r
fr+(s−1)v−(s−1)w,fr+(s−2)v+(s−2)w,...fr+2v−2w,fr+v−w,fr
f
r
+
s
v
−
s
w
,
f
r
+
(
s
−
1
)
v
−
(
s
−
1
)
w
,
.
.
.
f
r
+
2
v
−
2
w
,
f
r
+
v
−
w
,
f
r
f_{r + sv} - sw, f_{r + (s - 1)v} - (s - 1)w, ... f_{r + 2v} - 2w, f_{r + v} - w, f_r
fr+sv−sw,fr+(s−1)v−(s−1)w,...fr+2v−2w,fr+v−w,fr
可以发现每个序列的长度不超过
s
+
1
s+1
s+1,因为最多可以选
s
s
s个,然后不选也是一种情况,共
s
+
1
s + 1
s+1 种情况
于是通过该 滑动窗口 ,我们就能在 线性 的时间里求出 i 阶段里,所有满足
j
=
r
j = r % v
j=r 的
f
[
i
]
[
j
]
f[i][j]
f[i][j] 滑动窗口
求 最大值 的实现,只需利用 队列 在队头维护一个 最大值 的 单调递减 的 单调队列 即可
为了更新所有
i
i
i 阶段里的状态
f
[
i
]
[
j
]
f[i][j]
f[i][j],我们只需再额外枚举所有的 余数 r 即可
时间复杂度:O(
n
×
v
n × v
n×v )
空间复杂度:O(
n
×
v
n × v
n×v)
滑动窗口的长度为
s
+
1
s+1
s+1
for(int i = 0; i < n; i ++ )
{
memcpy(pre,f,sizeof f); //把上一层计算的状态复制过来
int v,s,w; //体积 次数 价值
v = read(), w = read(), s = read();
for(int j = 0; j < v; j ++ ) //枚举每一个余数 0~v-1
//枚举 %v 的余数 因为例如 当v = 3 ,f[0],f[1],f[2]是不同的集合
//当s = 3 f[12] 可以由 f[9],f[6],f[3] 转化而来 所以%v 余数相同的 f[j] 可以划分在同一个集合
{
int hh = 0, tt = -1;
for(int k = j; k <= m; k += v)
//因为j是 %v的余数 所以枚举 还原j, %v 就相当于减去了n 个 v 我们在不超过容积m的前提下,持续递增k
{
//利用单调队列
//1.超出滑动窗口的 弹出队列
if(hh <= tt && q[hh] < k - s * v) ++hh;
//当队列不空 当 滑动窗口的队头 离开了窗口,则将队头出队,
//因为s为最大数量,所以滑动窗口的最大长度为s 从当前位置k开始 往前推s个位置
//如果队头处于k-s*v之前 则队头离开了滑动窗口,将其出队
while(hh <= tt && pre[q[tt]] - (q[tt] - j) / v * w <= pre[k] - (k - j) / v * w ) tt--;
//当队列不空 为保持滑动窗口的单调递减 滑动窗口的尾值必须大于新入队的元素 否则将队尾出队
//队尾元素是 pre[j + k * v] - k * w, q[tt] == j + k * v - > (q[tt] - j )/v == k
if(hh <= tt ) f[k] = max(f[k],pre[q[hh]] + (k - q[hh]) / v * w);
//更新时 用 队头元素 + 中间间隔了多少个w 即k*w 来更新
//当队列不空时更新f f数组储存的是滑动窗口的最大值
//将新元素入队
q[++tt] = k;
}
}