这个博客想带大家通透的了解完全背包(我早就学完了)
完全背包是什么
相较于 01 背包,完全背包就是每个物品的数量有无数个,而 01 背包则是每个物品只能选一个。
怎么解决
我们定义
f
[
i
]
[
j
]
f[i][j]
f[i][j] 表示前
i
i
i 个物品,背包容量为
j
j
j 的最大价值。
若我们第
i
i
i 个物品一个也不选就是
f
[
i
−
1
]
[
j
]
f[i-1][j]
f[i−1][j] 这是很简单的。
但是假如我要选物品呢?
而这时有点难办:
为什么呢?
因为你不知道我们要挑选多少个,但是我们如果枚举那是不是太笨了呢?
而完全背包的发明者也巧妙地想到并解决了这个问题:
我们可以交给之前算过的
f
[
i
]
[
j
−
w
[
i
]
]
f[i][j-w[i]]
f[i][j−w[i]] (
w
[
i
]
w[i]
w[i] 表示当前物品的体积)
为什么呢,因为你不能像 01 背包一样直接写成
f
[
i
−
1
]
[
j
−
w
[
i
]
]
f[i-1][j-w[i]]
f[i−1][j−w[i]],
因为假如写的
i
−
1
i-1
i−1 就是直接只选了一遍你就走了但是完全背包有无数个那当然不行啊。
怎么优化
首先时间已经是不能优化了。
那么我们只能考虑优化空间。
滚动数组优化依然是可以的,但我们来着重讲讲降维优化,我们先看看代码:
for (int i = 1; i <= n; ++i) {
for (int j = w[i]; j <= m; ++j) f[j] = max(f[j], f[j - w[i]] + c[i]);
}
我们发现唯一不同的就是枚举顺序,01 背包是倒序枚举而完全背包是正序枚举。
我们来回顾一下 01 背包的优化:
f
[
i
]
[
j
]
=
m
a
x
(
f
[
i
−
1
]
[
j
]
,
f
[
i
−
1
]
[
j
−
w
[
i
]
]
+
c
[
i
]
)
f[i][j] = max(f[i-1][j],f[i-1][j-w[i]]+c[i])
f[i][j]=max(f[i−1][j],f[i−1][j−w[i]]+c[i])
而 01 背包要倒序枚举,为什么?
因为假如正序枚举 我们此时调用的
f
[
j
−
w
[
i
]
]
f[j-w[i]]
f[j−w[i]] 已经被更新过了。
所以需要倒序枚举。而此时我们回头一看,诶这不就是完全背包所需要的吗。完全背包调用的
f
[
i
]
[
j
−
w
[
i
]
]
f[i][j-w[i]]
f[i][j−w[i]] 就是要被更新过的啊!
蓦然回首,那人却在,灯火阑珊处。
拓展
完全背包求具体方案
题目大意如同其名。
我们做完全背包时可以记录每个物品的个数。
记录 p o s [ i ] [ j ] pos[i][j] pos[i][j] 表示第 i i i 个物品且当前背包容量为 j j j 的选的个数。
所以我们就假如转移到
f
[
i
]
[
j
−
w
[
i
]
]
+
c
[
i
]
f[i][j-w[i]]+c[i]
f[i][j−w[i]]+c[i] 的时候把
p
o
s
pos
pos 更新一下就可以了。
p
o
s
[
i
]
[
j
]
=
p
o
s
[
i
]
[
j
−
w
[
i
]
]
+
1
pos[i][j]=pos[i][j-w[i]]+1
pos[i][j]=pos[i][j−w[i]]+1
代码:
#include <bits/stdc++.h>
using namespace std;
const int N = 105, M = 1005;
int n, m, w[N], c[N], f[N][M], pos[N][M];
void print(int x, int y) {
if (!x) return;
if (!y) return;
if (pos[x][y]) cout << x << ' ' << pos[x][y] << endl, print(x - 1, y - w[x] * pos[x][y]);
else print(x - 1, y);
}
int main() {
cin >> n >> m;
for (int i = 1; i <= n; ++i) cin >> c[i] >> w[i];
for (int i = 1; i <= n; ++i) {
for (int j = 0; j <= m; ++j) {
f[i][j] = f[i - 1][j], pos[i][j] = 0;
if (j >= w[i] && f[i][j] < f[i][j - w[i]] + c[i]) f[i][j] = f[i][j - w[i]] + c[i], pos[i][j] = pos[i][j - w[i]] + 1;
}
}
cout << f[n][m] << endl;
print(n, m);
return 0;
}
最后说一句: