设有
n
n
n 件物品,背包容量为
m
m
m ,第
i
i
i 件物品的体积为
v
i
v_i
vi ,价值为
w
i
w_i
wi ,数量为
s
i
s_i
si ,求背包所能容纳的最大价值
朴素算法:点击链接
直接用
01
01
01 背包的朴素转移方程,时间复杂度为
O
(
n
m
s
)
O(nms)
O(nms) ,代码如下:
for(int i = 1;i <= n;i++)
for(int j = 1;j <= s[i];j++)
for(int k = m;k >= v[i];k--)
dp[k] = max(dp[k],dp[k-v[i]]+w[i]);
二进制拆分:点击这里
对物品进行二进制拆分之后再用
01
01
01 背包的转移方程直接转移,时间复杂度为
O
(
n
m
l
o
g
s
)
O(nmlogs)
O(nmlogs) ,代码如下:
for(int i = 1;i <= n;i++)
{
int base = 1;
while(s1[i] > base)
{
s1[i] -= base;
v[++cnt] = base*v1[i];
w[cnt] = base*w1[i];
base <<= 1;
}
if(s1[i])
{
v[++cnt] = s1[i]*v1[i];
w[cnt] = s1[i]*w1[i];
}
}
for(int i = 1;i <= cnt;i++)
for(int j = m;j >= v[i];j--)
dp[j] = max(dp[j],dp[j-v[i]]+w[i]);
单调队列优化:点击这里
我们先从最原始的转移方程入手,设
d
p
[
i
]
dp[i]
dp[i] 表示容量为
i
i
i 的背包的最大价值,此状态对于某一件指定物品有转移方程:
d
p
[
i
]
=
m
a
x
(
d
p
[
i
]
,
d
p
[
i
−
v
]
+
w
,
d
p
[
i
−
2
v
]
+
2
w
,
.
.
.
,
d
p
[
i
−
k
v
]
+
k
w
)
,
(
0
≤
k
≤
m
i
n
(
s
,
⌊
i
v
⌋
)
)
dp[i] = max(dp[i],dp[i-v]+w,dp[i-2v]+2w,...,dp[i-kv]+kw),(0\le k \le min(s,\lfloor \frac iv \rfloor))
dp[i]=max(dp[i],dp[i−v]+w,dp[i−2v]+2w,...,dp[i−kv]+kw),(0≤k≤min(s,⌊vi⌋))
设
i
=
a
+
b
v
i=a+bv
i=a+bv ,则有:
d
p
[
a
+
b
v
]
=
m
a
x
(
d
p
[
a
+
b
v
]
,
d
p
[
a
+
(
b
−
1
)
v
]
+
w
,
.
.
.
,
d
p
[
a
+
(
b
−
k
)
v
]
+
k
w
)
dp[a+bv]=max(dp[a+bv],dp[a+(b-1)v]+w,...,dp[a+(b-k)v]+kw)
dp[a+bv]=max(dp[a+bv],dp[a+(b−1)v]+w,...,dp[a+(b−k)v]+kw)
换元一下,令
k
=
b
−
k
k=b-k
k=b−k :
=
m
a
x
(
d
p
[
a
+
k
v
]
+
(
b
−
k
)
w
)
,
(
0
≤
k
≤
m
i
n
(
s
,
⌊
i
v
⌋
)
=max(dp[a+kv]+(b-k)w),(0\le k \le min(s,\lfloor \frac iv \rfloor)
=max(dp[a+kv]+(b−k)w),(0≤k≤min(s,⌊vi⌋)
从上个式子我们可以发现
d
p
[
a
+
b
v
]
dp[a+bv]
dp[a+bv] 是由
d
p
[
a
+
k
v
]
−
k
w
dp[a+kv]-kw
dp[a+kv]−kw (
b
w
bw
bw是常数可忽略对单调性的影响)最大的那个
d
p
[
a
+
k
v
]
dp[a+kv]
dp[a+kv] 转移过来的,因此我们可以对每一个余数
a
a
a 维护一个单调队列来进行状态的转移,因为对于每一个物品每一种体积最多只会进队出队一次,故其时间复杂度为
O
(
n
m
)
O(nm)
O(nm) ,代码如下:
for(int i = 1;i <= n;i++)
{
int v = read(),w = read(),s = read();
memcpy(g,dp,sizeof(dp));
for(int r = 0;r < v;r++)//枚举余数
{
int head = 0,tail = -1;
for(int k = 0;r+k*v <= m;k++)
{
if(head <= tail && k-qu[head] > s) head++;
while(head <= tail && g[r+k*v]-k*w >= g[r+qu[tail]*v]-qu[tail]*w) tail--;
qu[++tail] = k;
dp[r+k*v] = g[r+qu[head]*v]+(k-qu[head])*w;
}
}
}