2 条题解
-
0
这是一份标准的 AC 代码。
核心解题思路回顾
- 贪心策略:走到第 站时,如果当前站点的油价比之前遇到的最低油价
min_price还要低,就更新min_price。无论我们在哪个站点,加油时总是按“目前为止见过的最低价”来结算(相当于在之前便宜的站点预先多加了油)。 - 剩余油量管理:因为必须买整数升油,跑完一段路后往往会有剩余的油。这部分油(
rem_dist)要存起来,用于抵扣下一段路程。 - 数据类型:总金额和距离会超过
int范围,必须使用long long。
标准 C++ 代码
#include <iostream> #include <vector> #include <cmath> #include <algorithm> // 用于 min 函数 using namespace std; int main() { // 1. 优化输入输出速度(虽非必须,但对于大量数据是个好习惯) ios::sync_with_stdio(false); cin.tie(0); // 2. 定义变量 // n: 站点数, d: 每升油能跑的距离 // 注意:d 参与乘法运算,建议用 long long 防止溢出,虽然题目范围 1e5 int 也够 int n; long long d; cin >> n >> d; // 3. 读入数据 // v数组:存储站点间的距离,共有 n-1 段 // a数组:存储站点的油价,共有 n 个 vector<long long> v(n - 1); for (int i = 0; i < n - 1; ++i) { cin >> v[i]; } vector<int> a(n); for (int i = 0; i < n; ++i) { cin >> a[i]; } // 4. 模拟与贪心过程 long long ans = 0; // 总花费(必须 long long) long long rem_dist = 0; // 油箱里剩余油量能跑的距离(必须 long long) int min_price = a[0]; // 记录当前遇到的最低油价,初始为第1站的价格 // 遍历每一段路程 (从站点 1 到 站点 n-1) // i 代表当前所在的站点下标,我们要从 i 走到 i+1 for (int i = 0; i < n - 1; ++i) { // 【核心贪心】:到了站点 i,看看这里的油是不是比之前见过的都便宜 // 如果是,以后就按这里的价格算(相当于在这里多加点油) // 如果不是,保持之前的最低价(相当于用之前在便宜站点买的囤油) if (a[i] < min_price) { min_price = a[i]; } long long need_dist = v[i]; // 这一段路需要的距离 // 如果剩余油量足够跑完这段路 if (rem_dist >= need_dist) { rem_dist -= need_dist; } // 如果剩余油量不够 else { // 1. 算出还缺多少距离 long long lack = need_dist - rem_dist; // 2. 算出至少买多少升油 (向上取整) // 公式:(a + b - 1) / b 等价于 ceil(a / b) long long liters = (lack + d - 1) / d; // 3. 累加花费 ans += liters * min_price; // 4. 更新剩余油量 // 新的剩余 = (旧剩余 + 新买的距离) - 这一段消耗的距离 rem_dist += liters * d; rem_dist -= need_dist; } } // 5. 输出结果 cout << ans << endl; return 0; }易错点检查
- 向上取整公式:计算买油量时使用
(lack + d - 1) / d是最稳妥的整数运算写法,不要用ceil浮点数运算,容易有精度误差。 long long:ans(总花费)和rem_dist(累计剩余距离)一定要开long long,因为 会超过 。- 循环范围:距离数组
v只有n-1个元素,循环是0到n-2(对应站点 直到站点 )。
- 贪心策略:走到第 站时,如果当前站点的油价比之前遇到的最低油价
-
0
你好!我是你的 OI 教练。
这道题(CSP-J 2023 T2)是一道非常经典的贪心算法题目。只要你想通了“什么时候加油最划算”这个问题,代码其实非常短。
下面我为你梳理几个核心的解题思路和避坑指南:
1. 核心策略:贪心(寻找“当前最低价”)
题目告诉我们要从站点 1 开到站点 ,且油箱无限大。这意味着我们可以囤油。
想象一下你开到了第 个站点,你需要前往第 个站点。你现在的油箱里可能还有剩下的油,也可能不够了。如果不够,你必须加油。那么,用哪个站点的油价来算这笔钱呢?
- 如果你现在的站点 的油价,比之前经过的所有站点( 到 )都要便宜,那肯定是在当前站点 加油最划算。
- 如果你现在的站点 的油价很高,比你之前路过的某个站点(比如站点 )贵。既然油箱无限大,那你当初在站点 的时候,就应该多加一点油,这就相当于你现在用站点 的便宜价格在跑现在的路。
结论: 当我们走到第 个站点,准备去往第 个站点时,我们结算的油价应该是从第 1 站到第 站中出现的最低油价。 你需要维护一个变量(比如
min_price),记录“一路上见过的最便宜的油价”。2. 难点处理:整数升油与“剩余油量”
题目有一个非常讨厌的限制:只能购买整数升的油。 这意味着,如果你只需要跑 1 公里,而 1 升油能跑 10 公里,你也必须买 1 升油。跑完这 1 公里后,你的油箱里还剩相当于 9 公里的油。
这剩下的油不能浪费! 它必须被记录下来,用于抵扣下一段路程。
模拟过程:
- 定义一个变量
rem(remainder),记录当前油箱里的油还能跑多少公里(初始为 0)。 - 定义
min_price,记录目前为止最低油价。 - 循环遍历每一段路程(从 1 到 ):
- 更新
min_price:min_price = min(min_price, 当前站点油价)。 - 获取当前路段距离
dist = v[i]。 - 先用剩下的油跑:如果
rem >= dist,说明剩下的油够跑,不需要花钱。更新rem -= dist,直接进入下一轮。 - 如果剩下的油不够跑:
- 计算还需要跑多远:
need_dist = dist - rem。 - 计算需要买几升油:
num = ceil(need_dist / d)。- C++技巧:整数向上取整
(a + b - 1) / b。这里就是(need_dist + d - 1) / d。
- C++技巧:整数向上取整
- 计算花费:
ans += num * min_price。 - 关键更新:你的油箱里现在的油变了。
- 你加了
num升油,能跑num * d公里。 - 加上原来剩的
rem公里。 - 跑掉了
dist公里。 - 新剩余:
rem = rem + num * d - dist。
- 你加了
- 计算还需要跑多远:
- 更新
3. 数据范围与类型(必看!)
请看数据范围:
- 。
- 。
- 总路程可能会达到 公里。
警告:
- 普通的
int类型最大只能存约 。 - 总花费和总路程都会超过
int的范围。 - 必须使用
long long来存储你的答案(ans)!
4. 代码结构框架
#include <iostream> #include <cmath> using namespace std; int main() { // 1. 定义变量,注意数据范围,答案要用 long long int n; long long d; // d 用 long long 防止计算乘法溢出 cin >> n >> d; // 2. 读入距离数组 v[] 和 油价数组 a[] // 建议使用数组存储,方便循环访问 // 3. 初始化 long long ans = 0; // 总花费 long long rem = 0; // 剩余油量能跑的距离 int min_price = 1e9; // 当前最低油价,初始化为一个大数 // 4. 循环处理每一段路 (从站点 1 到 n-1) for (int i = 1; i <= n - 1; i++) { // a. 拿到当前站点的油价 a[i] // b. 更新 min_price = min(min_price, a[i]) // c. 拿到这一段的距离 v[i] (注意数组下标对应关系) // d. 判断 rem 是否足够 // 如果够:rem -= v[i] // 如果不够: // 计算缺多少距离 // 计算要买几升油 (向上取整) // 累加花费 ans += 买油数 * min_price // 更新 rem (加上新买的油能跑的距离,减去这段路程) } // 5. 输出 ans cout << ans << endl; return 0; }这道题其实就是在模拟开车的每一步,只要把“多出来的油留到下一段用”这个逻辑处理好,就一定能 AC。加油!
- 1
信息
- ID
- 14099
- 时间
- 1000ms
- 内存
- 32MiB
- 难度
- 3
- 标签
- 递交数
- 1
- 已通过
- 1
- 上传者