1. 概述:何为背包
背包问题 = 一些物品 (体积, 价值, 个数)+ 一个容量有限的背包 + 求最大价值。
1.1 01背包
01背包表示物品只能选0或1个,所以叫01背包。它是所有背包的基础。
1.1.1 普通版
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 30 + 10, M = 200 + 10;
/*
递推关系式 dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - w[i]] + c[i])
递推初值 dp[0][0] = 0
答案就是 dp[n][m]
*/
int m, n;
LL w[N], c[N];
LL dp[N][M];
int main() {
cin >> m >> n;
for (int i = 1; i <= n; i++) {
cin >> w[i] >> c[i];
}
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
dp[i][j] = dp[i - 1][j];
if (j - w[i] >= 0) {
dp[i][j] = max(dp[i][j], dp[i - 1][j - w[i]] + c[i]);
}
}
}
cout << dp[n][m] << "\n";
return 0;
}
dp[i][j]表示当背包容量为j、选取前i件物品时背包中的最大价值。
1.1.2 优化版
由于dp数组占用空间为,N若是开到1e5就爆了,所以可以使用滚动数组优化。
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 30 + 10, M = 200 + 10;
int m, n;
LL w[N], c[N];
LL dp[M];
int main() {
cin >> m >> n;
for (int i = 1; i <= n; i++) {
cin >> w[i] >> c[i];
}
for (int i = 1; i <= n; i++) {
for (int j = m; j >= w[i]; j--) {
dp[j] = max(dp[j], dp[j - w[i]] + c[i]);
}
}
cout << dp[m] << "\n";
return 0;
}
优化后dp数组直接去掉了一维,空间优化了不少,不过注意j要倒着循环。
1.2 完全背包
完全背包表示物品可以选无限个(其实并不是无限个,而是个)。
1.2.1 普通版
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 35, M = 210;
int m, n;
LL w[N], c[N];
LL dp[N][M];
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
cin >> m >> n;
for (int i = 1; i <= n; i++)
cin >> w[i] >> c[i];
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++)
for (int k = 0; k <= j / w[i]; k++) {
if (j - k * w[i] >= 0) {
dp[i][j] = max(dp[i][j], dp[i - 1][j - k * w[i]] + k * c[i]);
}
}
cout << "max=" << dp[n][m] << "\n";
return 0;
}
1.2.2 优化版
完全背包同样可以删维。
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 35, M = 210;
int m, n;
LL w[N], c[N];
LL dp[M];
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
cin >> m >> n;
for (int i = 1; i <= n; i++)
cin >> w[i] >> c[i];
for (int i = 1; i <= n; i++)
for (int j = w[i]; j <= m; j++)
dp[j] = max(dp[j], dp[j - w[i]] + c[i]);
cout << "max=" << dp[m] << "\n";
return 0;
}
1.3 多重背包
多重背包新增了一个变量s,表示物品有s个。所以01背包在一定程度上也算是多重背包。
1.3.1 普通版
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 1010, M = 2010;
int n, m;
LL w[N], c[N], s[N];
LL dp[N][M];
int main() {
cin >> n >> m;
for (int i = 1; i <= n; i++) {
cin >> w[i] >> c[i] >> s[i];
}
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
for (int k = 0; k <= s[i]; k++) {
if (j - k * w[i] >= 0) {
dp[i][j] = max(dp[i][j], dp[i - 1][j - k * w[i]] + k * c[i]);
}
}
}
}
cout << dp[n][m] << "\n";
return 0;
}
1.3.2 优化版
多重背包不仅可以删维,还可以使用二进制优化。
1.3.2.1 二进制优化概念
用 1, 2, 4, 8, 16, 32, 64 可以凑出 0 ∼ 127(= 1 + 2 + ... + 64) 的所有数字。
用 1, 2, 4, 8, 16, 32, 64, 73 可以凑出 0 ∼ 200(= 1 + 2 + ... + 64 + 73) 的所有数字。
所以,s 可以用 1, 2, 4, ..., 2 k , C (0 ≤ C < 2 k+1) 凑出, 用上述思想, 便将问题转化成了 01 背包。
1.3.2.2 二进制优化代码
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 1010 * 13, M = 2010;
int n, m, cnt;
LL w[N], c[N];
LL dp[M];
int main() {
cin >> n >> m;
for (int i = 1; i <= n; i++) {
int a, b, s;
cin >> a >> b >> s;
int k = 1;
while (s - k >= 0) {
s -= k;
++cnt;
w[cnt] = k * a;
c[cnt] = k * b;
k *= 2;
}
if (s) {
++cnt;
w[cnt] = s * a;
c[cnt] = s * b;
}
}
for (int i = 1; i <= cnt; i++) {
for (int j = m; j >= w[i]; j--) {
dp[j] = max(dp[j], dp[j - w[i]] + c[i]);
}
}
cout << dp[m] << "\n";
return 0;
}
1.4 混合背包
混合背包就是把上述背包组合起来(像废话)。
思路就是把完全背包和01背包转化为多重背包(数量分别为和个,上面也提到过)。
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 40, M = 210;
int m, n;
LL w[N], c[N], p[N], dp[N][M];
int main() {
cin >> m >> n;
for (int i = 1; i <= n; i++) {
cin >> w[i] >> c[i] >> p[i];
}
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++) {
if (p[i] == 0) {
for (int k = 0; k <= j / w[i]; k++) {
if (j - k * w[i] >= 0) {
dp[i][j] = max(dp[i][j], dp[i - 1][j - k * w[i]] + k * c[i]);
}
}
} else {
for (int k = 0; k <= p[i]; k++) {
if (j - k * w[i] >= 0) {
dp[i][j] = max(dp[i][j], dp[i - 1][j - k * w[i]] + k * c[i]);
}
}
}
}
cout << dp[n][m] << "\n";
return 0;
}
2. 结尾
好了,文章内容就到这里了,谢谢大家~