参考 洛谷P1776 宝物筛选_NOI导刊2010提高(02)(多重背包,单调队列)
d
p
[
i
]
[
j
]
dp[i][j]
dp[i][j] 为前
i
i
i 个物品,重量不超过
j
j
j 的最大价值,
w
w
w 为重量,
v
v
v 为价值,
m
m
m 为数量,
W
W
W 为背包大小
d
p
[
i
]
[
j
]
=
m
a
x
{
d
p
[
i
−
1
]
[
j
−
k
w
i
]
+
k
v
i
}
0
<
=
k
<
=
m
i
n
(
m
i
,
j
w
i
)
dp[i][j] = max\{dp[i-1][j-kw_i] + kv_i\} \qquad 0<=k<=min(m_i,\frac{j}{w_i})
dp[i][j]=max{dp[i−1][j−kwi]+kvi}0<=k<=min(mi,wij)
去掉第一维后,简化为:
d
p
[
j
]
=
m
a
x
{
d
p
[
j
−
k
w
]
+
k
v
}
dp[j] = max\{dp[j-kw] + kv\}
dp[j]=max{dp[j−kw]+kv}
时间复杂度:
O
(
n
m
W
)
O(nmW)
O(nmW)
仔细观察对于一个
j
j
j 而言,只有
j
w
i
\frac{j}{w_i}
wij 个有效转移,那么就可以去掉很多无用的状态
设
j
=
k
1
w
+
d
j = k_1w+d
j=k1w+d ,那么枚举
d
d
d ,再枚举
k
1
k_1
k1 ,再枚举
k
k
k,复杂度是一样的
d
p
[
j
]
=
m
a
x
{
d
p
[
k
1
w
+
d
−
k
w
]
+
k
v
}
dp[j] = max\{dp[k_1w+d-kw] + kv\}
dp[j]=max{dp[k1w+d−kw]+kv}
=
m
a
x
{
d
p
[
(
k
1
−
k
)
w
+
d
]
−
(
k
1
−
k
)
v
}
+
k
1
v
= max\{dp[(k_1-k)w+d] - (k_1-k)v\} + k_1v
=max{dp[(k1−k)w+d]−(k1−k)v}+k1v
注意
k
1
k_1
k1 和
k
k
k 是无关的,所以可以分离出来
因此对于一个
d
d
d 而言,有意义的状态只有
W
−
d
w
\frac{W-d}{w}
wW−d 种,算上
d
d
d ,总的状态只有
O
(
W
)
O(W)
O(W)
设
g
k
=
d
p
[
k
w
+
d
]
−
k
v
g_k = dp[kw+d] - kv
gk=dp[kw+d]−kv
由
0
<
=
k
<
=
m
i
n
(
m
,
j
w
)
0<=k<=min(m,\frac{j}{w})
0<=k<=min(m,wj),
j
w
=
k
1
w
+
d
w
=
k
1
+
d
w
>
k
1
\frac{j}{w} = \frac{k_1w+d}{w} =k_1+\frac{d}{w} >k_1
wj=wk1w+d=k1+wd>k1
得
m
a
x
{
0
,
k
1
−
m
}
<
=
k
1
−
k
<
=
k
1
)
max\{0,k_1-m\}<=k_1-k<=k_1)
max{0,k1−m}<=k1−k<=k1)
也就是对于每个
k
1
k_1
k1,只能从
m
a
x
{
g
k
∣
k
∈
[
m
a
x
{
0
,
k
1
−
m
}
,
k
1
]
}
max\{g_k|k∈[max\{0,k_1−m\},k_1]\}
max{gk∣k∈[max{0,k1−m},k1]} 中转移
就不用枚举
k
k
k了,用单调队列优化维护下标在
[
k
1
−
m
,
k
1
]
[k_1−m,k_1]
[k1−m,k1] 范围内的决策值即可
时间复杂度就是惊为天人的
O
(
n
W
)
O(nW)
O(nW)
模板题:P1776 宝物筛选
#include<bits/stdc++.h>
#define rint register int
#define deb(x) cerr<<#x<<" = "<<(x)<<'\n';
using namespace std;
typedef long long ll;
const int maxn = 1e2 + 5;
const int maxw = 4e4 + 5;
int n, W, dp[maxn][maxw];
int v[maxn], w[maxn], m[maxn];
int q[maxw][2];
int main() {
scanf("%d%d", &n, &W);
for(int i=1; i<=n; i++) {
scanf("%d%d%d", v+i, w+i, m+i);
for(int d=0; d<w[i]; d++) {
int head = 1, tail = 0;
int k1 = (W - d) / w[i];
for(int k=0; k<=k1; k++) {
while(head<=tail && q[tail][1]<=dp[i-1][d+k*w[i]]-k*v[i]) tail--;
q[++tail][0] = k, q[tail][1] = dp[i-1][d+k*w[i]]-k*v[i];
while(head<=tail && q[head][0] < k-m[i]) head++;
dp[i][d+k*w[i]] = max(dp[i][d+k*w[i]], q[head][1] + k*v[i]);
}
}
}
printf("%d\n", dp[n][W]);
}