




















CSP-J 2023真题“公路”是一道典型的贪心算法题。本题要求计算出在不同站点油价不同的情况下,从起点行驶到终点的最小花费。适合 GESP 四级及以上考生练习,难度⭐⭐,洛谷难度等级普及-。
小苞准备开着车沿着公路自驾。
公路上一共有 $n$ 个站点,编号为从 $1$ 到 $n$。其中站点 $i$ 与站点 $i + 1$ 的距离为 $v_i$ 公里。
公路上每个站点都可以加油,编号为 $i$ 的站点一升油的价格为 $a_i$ 元,且每个站点只出售整数升的油。
小苞想从站点 $1$ 开车到站点 $n$,一开始小苞在站点 $1$ 且车的油箱是空的。已知车的油箱足够大,可以装下任意多的油,且每升油可以让车前进 $d$ 公里。问小苞从站点 $1$ 开到站点 $n$,至少要花多少钱加油?
输入的第一行包含两个正整数 $n$ 和 $d$,分别表示公路上站点的数量和车每升油可以前进的距离。
输入的第二行包含 $n - 1$ 个正整数 $v_1, v_2\dots v_{n-1}$,分别表示站点间的距离。
输入的第三行包含 $n$ 个正整数 $a_1, a_2 \dots a_n$,分别表示在不同站点加油的价格。
输出一行,仅包含一个正整数,表示从站点 $1$ 开到站点 $n$,小苞至少要花多少钱加油。
1
2
3
5 4
10 10 10 10
9 8 9 6 5
【样例 1 解释】
最优方案下:小苞在站点 $1$ 买了 $3$ 升油,在站点 $2$ 购买了 $5$ 升油,在站点 $4$ 购买了 $2$ 升油。
【数据范围与约定】
对于所有测试数据保证:$1 \leq n \leq 10^5$,$1 \leq d \leq 10^5$,$1 \leq v_i \leq 10^5$,$1 \leq a_i \leq 10^5$。
| 测试点 | $n \leq$ | 特殊性质 |
|---|---|---|
| $1\sim 5$ | $8$ | 无 |
| $6\sim 10$ | $10^3$ | 无 |
| $11\sim 13$ | $10^5$ | A |
| $14\sim 16$ | $10^5$ | B |
| $17\sim 20$ | $10^5$ | 无 |
本题要求计算从小苞的起点(站点 $1$)行驶到终点(站点 $n$)的最低花费。因为油箱的容量是无限的,这是一道经典的贪心算法题。
由于可以在前面的任何一个站点提前购买后续所需的油,因此在任意位置,我们都应该选择在已访问过的站点中,油价最低的那个进行加油。
也就是说,我们在模拟行驶的过程中:
cur_min_price,表示从起点到当前站点所经过的最低油价。cur_min_price。我们可以从站点 $1$ 开始,模拟向终点行驶:
cur_min_price。初始时 cur_min_price = a[1]。leftover_dist。初始时 leftover_dist = 0。leftover_dist 与站点 $i$ 到 $i+1$ 的距离 $v_i$。leftover_dist < v_i,说明需要补充油量。所需补充的距离为 needed_dist = v_i - leftover_dist。ans += liters * cur_min_price。leftover_dist += liters * d。leftover_dist -= v_i。cur_min_price = min(cur_min_price, a[i+1])。int 类型上限约为 $2 \times 10^9$。因此,表示距离、剩余可行驶距离以及总花费的变量必须使用 long long 类型,否则会导致数值溢出而计算错误。时间复杂度:由于只需对站点进行一次单向遍历,算法的时间复杂度为 $O(n)$,空间复杂度为 $O(n)$,可以高效通过全部测试点。
以下是使用 C++ 实现的代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
#include <iostream>
#include <vector>
#include <algorithm>
int main() {
// 优化输入输出流性能
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
int n;
long long d;
std::cin >> n >> d;
// v[i] 表示第 i 个站点与第 i + 1 个站点之间的距离
std::vector<long long> v(n);
for (int i = 1; i < n; ++i) {
std::cin >> v[i];
}
// a[i] 表示第 i 个站点的油价
std::vector<long long> a(n + 1);
for (int i = 1; i <= n; ++i) {
std::cin >> a[i];
}
long long ans = 0; // 累计总花费
long long leftover_dist = 0; // 剩余油量可行驶的距离
long long cur_min_price = a[1]; // 当前遇到的最低油价
for (int i = 1; i < n; ++i) {
// 如果剩余的油不够行驶到下一个站点,则在最低价站点加油
if (leftover_dist < v[i]) {
long long needed_dist = v[i] - leftover_dist;
// 计算需要购买的整升油量(向上取整)
long long liters = (needed_dist + d - 1) / d;
ans += liters * cur_min_price;
leftover_dist += liters * d;
}
// 减去走到下一个站点消耗的距离
leftover_dist -= v[i];
// 更新到达下一个站点后的最低油价
cur_min_price = std::min(cur_min_price, a[i + 1]);
}
std::cout << ans << "\n";
return 0;
}
本题的关键在于利用“油箱容量无限”这一特点,将分段购买的问题转化为“在已知的最低价站点提前购买”的贪心决策。在编写代码时,合理分析物理量可能达到的最大值并使用 long long 避免溢出,是解决此类模拟和贪心问题需要注意的细节。
所有代码已上传至Github:https://github.com/lihongzheshuai/yummy-code
GESP 学习专题站:GESP WIKI
"luogu-"系列题目可在洛谷题库进行在线评测。
"bcqm-"系列题目可在编程启蒙题库进行在线评测。
欢迎加入:Java、C++、Python技术交流QQ群(982860385),大佬免费带队,有问必答
欢迎加入:C++ GESP/CSP认证学习QQ频道,考试资源总结汇总
欢迎加入:C++ GESP/CSP学习交流QQ群(688906745),考试认证学员交流,互帮互助
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。