CCF CAT 训练二
A-Flower
因为时间各不相同,所以时间是假的,直接去最大\(k\)个就行
#include<bits/stdc++.h>
using namespace std;
#define int long long
using vi = vector<int>;
using i32 = int32_t;
using pii = pair<int, int>;
using ldb = long double;
const int inf = 1e9;
i32 main() {
ios::sync_with_stdio(false), cin.tie(nullptr);
int n, k;
cin >> n >> k;
vi t(n), v(n);
for (auto &i: t) cin >> i;
for (auto &i: v) cin >> i;
sort(v.begin(), v.end(), greater<int>());
cout << accumulate(v.begin(), v.begin() + k, 0ll) << "\n";
return 0;
}
B-Tree
最容易想到的方法就是真的把树建出来
#include<bits/stdc++.h>
using namespace std;
#define int long long
using vi = vector<int>;
using i32 = int32_t;
using pii = pair<int, int>;
struct node {
int v;
node *l, *r;
node(int v = 0, node *l = nullptr, node *r = nullptr) : v(v), l(l), r(r) {};
} *root = nullptr;
i32 main() {
ios::sync_with_stdio(false), cin.tie(nullptr);
int n;
cin >> n;
for (int i = 0, x; i < n; i++) {
cin >> x;
auto it = &root;
while (*it != nullptr) {
if (x < (*it)->v) it = &((*it)->l);
else it = &((*it)->r);
}
*it = new node(x);
}
for (int i = 0, x, dep; i < n; i++) {
cin >> x, dep = 1;
auto it = root;
while (it != nullptr) {
if (x < it->v) it = it->l;
else it = it->r;
dep++;
}
cout << dep << "\n";
}
return 0;
}
C-Best Travel Plans
首先我们可以求出前缀消耗的时间,然后就可以知道前缀可以娱乐的时间。
因为\(E_i\)不大,所以我们可以把所有娱乐的收益插入堆中,然后动态保证堆的大小始终小于等于前缀剩下的可以娱乐的时间,所以我们每次从堆中删除时删除最小值即可。然后我们在动态维护堆内元素和即可。
#include<bits/stdc++.h>
using namespace std;
using vi = vector<int>;
using i32 = int32_t;
using pii = pair<int, int>;
using ldb = long double;
const int inf = 1e9;
const int mod = 1e9 + 7;
i32 main() {
ios::sync_with_stdio(false), cin.tie(nullptr);
int n, m;
cin >> n >> m;
vi t(n), e(n);
for (int i = 1; i < n; i++) cin >> t[i];
for (auto &i: e) cin >> i;
priority_queue<int, vector<int>, greater<int>> q;
int cnt = 0, res = 0;
for (int i = 0, d; i < n; i++) {
cin >> d;
m -= t[i];
if (m <= 0) break;
while (!q.empty() and q.size() > m) cnt -= q.top(), q.pop();
while (e[i] > 0) {
if (q.size() == m) {
if (e[i] <= q.top()) break;
cnt -= q.top(), q.pop();
cnt += e[i], q.push(e[i]), e[i] -= d;
} else {
cnt += e[i], q.push(e[i]), e[i] -= d;
}
}
res = max(res, cnt);
}
cout << res << "\n";
return 0;
}
D-Hearthstone
就是普通的线性dp,\(f[i][x][y]\)前\(i\)位,第\(i\)位消耗法力\(y\),第\(i-1\)位消耗法力\(x\)的最大收益。
每次只要枚举最后三位的状态\(x,y,z\),然后判断是否合法,如果合法就存在转移\(f[i][y][z] = f[i-1][x][y] + v[i][z]\)
但是注意题目要求
他希望把构造好的n张卡牌首尾相连后,任何一个位置的卡牌都要比相邻的两张卡牌的法力消耗值都高或都低
也就说第\(n\)位和第\(1\)是相邻的,解决方法就是每次钦定第一位选什么,做三次dp。
#include<bits/stdc++.h>
using namespace std;
#define int long long
using vi = vector<int>;
using i32 = int32_t;
using pii = pair<int, int>;
using ldb = long double;
const int inf = 1e9;
const int mod = 1e9 + 7;
i32 main() {
ios::sync_with_stdio(false), cin.tie(nullptr);
int n;
cin >> n;
vector<array<int, 3>> v(n);
for (auto &it: v)
for (auto &i: it) cin >> i;
int res = -inf;
for (int T = 0; T < 3; T++) {
vector<vector<vector<int>>> f(n, vector<vector<int>>(3, vector<int>(3, -inf)));
for (int y = 0; y < 3; y++)
if (T != y) f[1][T][y] = v[0][T] + v[1][y];
for (int i = 2; i < n; i++)
for (int x = 0; x < 3; x++)
for (int y = 0; y < 3; y++) {
if (x == y or f[i - 1][x][y] < 0) continue;
for (int z = 0; z < 3; z++)
if (y != z and ((y > x) == (y > z)))
f[i][y][z] = max(f[i][y][z], f[i - 1][x][y] + v[i][z]);
}
for (int y = 0; y < 3; y++)
for (int z = 0; z < 3; z++)
if (y != z and z != T and ((z > y) == (z > T))) res = max(res, f.back()[y][z]);
}
cout << res << "\n";
return 0;
}
E-Hotpot
两侧的桌子实际上是独立的,我们先算一侧然后乘二就行
\(f[i][0/1]\)表示前\(i\)位且第\(i\)位坐不坐人的方案数,然后就是简单dp了
#include<bits/stdc++.h>
using namespace std;
#define int long long
using vi = vector<int>;
using i32 = int32_t;
using pii = pair<int, int>;
using ldb = long double;
const int inf = 1e9;
const int mod = 1e9 + 7;
i32 main() {
ios::sync_with_stdio(false), cin.tie(nullptr);
int n;
cin >> n;
vector<array<int, 2>> f(n + 1);
f[1][1] = f[1][0] = 1;
for (int i = 2; i <= n; i++) {
f[i][1] = f[i - 1][0];
f[i][0] = (f[i - 1][0] + f[i - 1][1]) % mod;
}
int res = (f[n][0] + f[n][1]) % mod;
res = res * res % mod;
cout << res << "\n";
return 0;
}