小奇挖矿2
小奇挖矿2 - 题解
题目大意
现在有 \(m + 1\) 个星球,从左到右标号为 \(0\) 到 \(m\),小奇最初在 \(0\) 号星球。
有 \(n\) 处矿体,第 \(i\) 处矿体有 \(a_i\) 单位原矿,在第 \(b_i\) 个星球上。
由于飞船使用的是老式的跳跃引擎,每次它只能从第 \(x\) 号星球移动到第 \(x + 4\) 号星球或 \(x + 7\) 号星球。每到一个星球,小奇会采走该星球上所有的原矿,求小奇能采到的最大原矿数量。
注意,小奇不必最终到达 \(m\) 号星球。
大概思路
Part 1 (20 pts)
既然第一个条件给了 \(n = 1\),所以只需要判断这个点会不会被走到就行了。
判断函数:
bool check(int n) {
for (int i = 1; i * 7 <= n; i ++) {
if ((n - i * 7) % 4 == 0) {
return 1;
}
} return 0;
}
Part 2 (40 pts)
详情
说实话,我真的不会。跳过(慌Part 3 (60 pts)
我一看,\(m \le 10 ^ 5\)。这不就是妥妥的 \(dp\) 嘛。
代码:
#include <bits/stdc++.h>
#define Maxn 100005
using namespace std;
int n, m, a[Maxn], b[Maxn], dp[Maxn], maxl;
bool check(int x) {
for (int i = 0; i <= x / 7; i ++) {
if ((x - i * 7) % 4 == 0) {
return 1;
}
} return 0;
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0);
cin >> n >> m;
for (int i = 1; i <= n; i ++) {
cin >> a[i] >> b[i];
if (check(b[i])) { // 切勿漏掉
dp[b[i]] += a[i];
}
}
for (int i = 0; i <= m; i ++) {
int now = dp[i];
if (i >= 4) {
dp[i] = max(dp[i], now + dp[i - 4]);
} if (i >= 7) {
dp[i] = max(dp[i], now + dp[i - 7]);
} maxl = max(maxl, dp[i]);
} // 纯纯dp
cout << maxl;
return 0;
}
Part 4 (70 pts)
枚举后可以知道,点 \(x\) 不能走到的点 \(p_x\) 有 $p_x \in {(x + 1),(x + 2),(x + 3),(x + 5),(x + 6),(x + 9),(x + 10),(x + 13),(x + 17)} $。
所以我们得到了一个更快的 \(check\) 函数,而且不仅仅适用于起点为 \(0\) 了。
新版 \(check\) 函数:
bool check(int x, int y) {
if (x == y + 1 || x == y + 2 || x == y + 3 ||
x == y + 5 || x == y + 6 || x == y + 9 ||
x == y + 10 || x == y + 13 || x == y + 17) {
return 0;
} return 1;
} // 判断y那能否走到x
容易得到:
\[dp_i = \min_{1 \le j < i}(dp_j + w_i)
\]
根据新的递推式可以得到代码:
#include <bits/stdc++.h>
#define Maxn 100005
using namespace std;
int n, m, tot = 0, dp[Maxn], maxl;
struct Point {
int x, num;
} a[Maxn], p[Maxn];
bool check(int x, int y) {
if (x == y + 1 || x == y + 2 || x == y + 3 ||
x == y + 5 || x == y + 6 || x == y + 9 ||
x == y + 10 || x == y + 13 || x == y + 17) {
return 0;
} return 1;
}
bool cmp(Point a, Point b) {
return a.x <= b.x;
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0);
cin >> n >> m;
for (int i = 1; i <= n; i ++) {
cin >> a[i].num >> a[i].x;
} sort(a + 1, a + n + 1, cmp);
for (int i = 1; i <= n; i ++) {
if (check(a[i].x, 0)) {
if (a[i].x == p[tot].x) {
p[tot].num += a[i].num;
} else {
tot ++, p[tot] = a[i];
}
}
}
// for (int i = 1; i <= tot; i ++) {
// cout << p[i].x << " " << p[i].num << "\n";
// }
for (int i = 1; i <= tot; i ++) {
dp[i] = p[i].num;
for (int j = 1; j < i; j ++) {
if (check(p[i].x, p[j].x)) {
// cout << i << " " << j << " : " << dp[i] << " " << dp[j] << " " << p[i].num << " ";
dp[i] = max(dp[i], dp[j] + p[i].num);
// cout << dp[i] << "\n";
}
} maxl = max(maxl, dp[i]);
}
cout << maxl;
return 0;
}
吐槽:将近一倍的脑细胞消耗量多换来了 \(10pts\)(不值。这边认为考试的时候可以把 \(60 pts\) 拿到就很可以了。
Part 5 (100 pts)
终于迎来了正解。
我们发现,\(\texttt{Part 4}\) 的时间复杂度为 \(O(n ^ 2)\)。所以只有 \(70 pts\)。
观察递推式,发现有一项是不变的,所以有如下推导:
\[\begin{aligned}
dp_i = &\min_{1 \le j < i}(dp_j + w_i) \\
&\min_{1 \le j < i}(dp_j) + w_i
\end{aligned}
\]
可以联想到单调队列优化DP和优先队列优化DP。我这里写的是优先队列优化DP。
#include <bits/stdc++.h>
#define Maxn 100005
using namespace std;
int n, m, tot = 0, dp, maxl;
struct Point {
int x, num;
bool operator <(const Point a) const{
return num < a.num;
}
} a[Maxn], p[Maxn], tmp[Maxn], t;
int del = 0;
priority_queue<Point> q;
bool check(int x, int y) {
if (x == y + 1 || x == y + 2 || x == y + 3 ||
x == y + 5 || x == y + 6 || x == y + 9 ||
x == y + 10 || x == y + 13 || x == y + 17) {
return 0;
} return 1;
}
bool cmp(Point a, Point b) {
return a.x <= b.x;
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0);
cin >> n >> m;
for (int i = 1; i <= n; i ++) {
cin >> a[i].num >> a[i].x;
} sort(a + 1, a + n + 1, cmp);
for (int i = 1; i <= n; i ++) {
if (check(a[i].x, 0)) {
if (a[i].x == p[tot].x) {
p[tot].num += a[i].num;
} else {
tot ++, p[tot] = a[i];
}
}
}
// for (int i = 1; i <= tot; i ++) {
// cout << p[i].x << " " << p[i].num << "\n";
// }
tmp[0].num = tmp[0].x = 0;
q.push(tmp[0]);
for (int i = 1; i <= tot; i ++) {
dp = del = 0;
while (!q.empty()) {
t = q.top(), q.pop();
tmp[++ del] = t;
if (check(p[i].x, t.x)) {
dp = t.num + p[i].num;
break;
}
}
for (int j = 1; j <= del; j ++) {
q.push(tmp[j]);
} // 补充点的信息
tmp[0].num = dp, tmp[0].x = p[i].x;
q.push(tmp[0]); // 新增点的信息
}
cout << q.top().num;
return 0;
}
吐槽:谁家良心考试第一题这么变态。
此篇结。

浙公网安备 33010602011771号