背包问题
给定一组物品,每种物品都有自己的重量和价值,现有一个背包,能承受的重量有限,在受限制的重量下,取若干物品,使得总价值最大。这一类问题,被称为背包问题。
01背包(物品个数为1)


for (int i = 1; i <= N; ++i) {
for (int j = 0; j <= V; ++j) {
if(j >= c[i]) {
dp[i][j] = max(dp[i - 1][j - c[i]] + w[i], dp[i - 1][j]);
} else {
dp[i][j] = dp[i - 1][j];
}
}
}
时间上是两重循环,时间复杂度为O(NV)。空间是二维的,空间复杂度也为O(NV)。

for (int i = 1; i <= n; ++i)
for (int j = v; j >= c[i]; --j) {
dp[j] = max(dp[j - c[i]] + w[i], dp[j]);
}
这个做法空间复杂度也为O(V)。
多重背包(物品个数有限)


for (int i = 1; i <= N; i++) {
for (int j = 0; j <= V; j++) {
for (int k = 0; k <= n[i]; k++) {
if (j >= c[i] * k) {
dp[i][j] = max(dp[i - 1][j - c[i] * k] + w[i] * k, dp[i][j]);
}
}
}
}
这一份代码和01背包相比,不再有else部分了,因为,k = 0的时候dp[i][j] = max(dp[i - 1][j], dp[i][j]), 相当于01背包的else部分。
空间优化
既然多重背包的可以转换成01背包,那么我们必然也可以像01背包那样优化空间复杂度。还是按照从大到小的顺序枚举背包体积。
for (int i = 1; i <= N; i++) {
for (int j = V; j >= 0; j--) {
for (int k = 1; k <= n[i]; k++) {
if (j >= c[i] * k) {
dp[j] = max(dp[j - c[i] * k] + w[i] * k, dp[j]);
}
}
}
}
完全背包(物品个数无限)

解析
虽然物品个数是无限的,但是实际上,由于背包容量有上限,每个物品最多选取的个数也是有限制的,这样可以转换成多重背包问题,进而可以转换成01背包问题。
可以用多重背包的思想来解决完全背包。
for (int i = 1; i <= N; i++) {
for (int j = 0; j <= V; j++) {
for (int k = 0; k * c[i] <= j; k++) {
dp[i][j] = max(dp[i - 1][j - c[i] * k] + w[i] * k, dp[i][j]);
}
}
}

for (int i = 1; i <= n; i++) {
for (int j = 0; j <= v; j++) {
if (j >= c[i]) {
dp[i][j] = max(dp[i][j - c[i]] + w[i], dp[i - 1][j]);
} else {
dp[i][j] = dp[i - 1][j];
}
}
}
这样我们的的算法时间复杂度O(NV),空间复杂度O(NV)。
不难发现,我们也可以把空间复杂度优化下来,优化成O(V)。
for (int i = 1; i <= n; i++) {
for (int j = c[i]; j <= v; j++) {
dp[j] = max(dp[j - c[i]] + w[i], dp[j]);
}
}
与 01 背包相比,完全背包只是第二重循环的顺序发生了翻转。
多重背包的二进制优化



1 #include <iostream>
2 using namespace std;
3 int n[110], c[110], w[110];
4 int nc[1000], nw[1000];
5 int dp[5010];
6 int main() {
7 int N, V;
8 cin >> N >> V;
9 for (int i = 1; i <= N; ++i) {
10 cin >> w[i] >> c[i] >> n[i];
11 }
12 int ncnt = 0;
13 // 二进制拆分
14 for (int i = 1; i <= N; ++i) {
15 int k;
16 // 找到最大的 k
17 for (k = 1; n[i] - (1 << k) + 1 > 0; ++k) {
18 nc[ncnt] = (1 << (k - 1)) * c[i];
19 nw[ncnt] = (1 << (k - 1)) * w[i];
20 ++ncnt;
21 }
22 --k;
23 // 最后一组
24 nc[ncnt] = (n[i] - (1 << k) + 1) * c[i];
25 nw[ncnt] = (n[i] - (1 << k) + 1) * w[i];
26 ++ncnt;
27 }
28 // 01 背包
29 for (int i = 0; i < ncnt; ++i) {
30 for (int j = V; j >= nc[i]; --j) {
31 dp[j] = max(dp[j], dp[j - nc[i]] + nw[i]);
32 }
33 }
34 cout << dp[V] << endl;
35 return 0;
36 }
-
来源:https://www.cnblogs.com/jiamian/p/12209162.html