20230417 训练记录:dp
你也许会好奇为什么没有 04.16 的训练记录,昨天农了一天 O(∩_∩)O。
Vacation
小明在接下来的 \(n\) 天,可以选择三种事件并获得 \(a_i / b_i / c_i\) 的快乐值,但是他不能连续两天及以上做同样的事。问最大的欢乐值是多少?
\(n \leq 10^5; a_i, b_i, c_i \leq 10^4\)。
换句话说就是每天做的事情都不一样。用 \(f_{i, j}\) 表示第 \(i\) 天做 \(j\) 事件所得到的最大快乐值,枚举今天和明天做的事件,即:
\[f_{i + 1, j} = \max\{ f_{i + 1, j}, f_{i, k} + \{a, b, c\}_k \}, j \ne k
\]
当然空间可以优化掉一维。
展开代码
#include <bits/stdc++.h>
using ll = long long;
int main() {
std::cin.tie(nullptr)->sync_with_stdio(false);
int n;
std::cin >> n;
std::array<int, 3> f{};
for (int i = 0; i < n; i++) {
std::array<int, 3> c{};
for (int &i : c) std::cin >> i;
std::array<int, 3> nf{};
for (int j = 0; j < 3; j++) {
for (int k = 0; k < 3; k++) if (j != k) {
nf[j] = std::max(nf[j], f[k] + c[j]);
}
}
f = nf;
}
std::cout << *std::max_element(f.begin(), f.end()) << '\n';
return 0;
}
Knapsack 1
01 背包。
\(n \leq 100, w \leq 10^5, v_i \leq 10^9\)。
展开代码
#include <bits/stdc++.h>
using ll = long long;
int main() {
std::cin.tie(nullptr)->sync_with_stdio(false);
int n, w;
std::cin >> n >> w;
std::vector<ll> f(w + 1);
for (int i = 0; i < n; i++) {
int wi, ci;
std::cin >> wi >> ci;
for (int j = w; j >= wi; j--) {
f[j] = std::max(f[j], f[j - wi] + ci);
}
}
std::cout << f.back() << '\n';
return 0;
}
Knapsack 2
01 背包。
\(n \leq 100, w \leq 10^9, v_i \leq 10^3\)。
考虑 \(f_{i, j}\) 为前 \(i\) 个物品,得到 \(j\) 价值的最小容量。
展开代码
#include <bits/stdc++.h>
using ll = long long;
int main() {
std::cin.tie(nullptr)->sync_with_stdio(false);
int n, w;
std::cin >> n >> w;
const int N = n * 1000;
std::vector<std::vector<int>> f(n + 1, std::vector<int>(N + 1, 0x3f3f3f3f));
f[0][0] = 0;
for (int i = 0; i < n; i++) {
int wi, vi;
std::cin >> wi >> vi;
for (int j = 0; j <= N; j++) {
f[i + 1][j] = std::min(f[i + 1][j], f[i][j]);
if (j + vi <= N) {
f[i + 1][j + vi] = std::min(f[i + 1][j + vi], f[i][j] + wi);
}
}
}
for (int i = N; ~i; i--) {
if (f[n][i] <= w) {
std::cout << i << '\n';
std::exit(0);
}
}
return 0;
}