(自用)CSP-J-2023-2 公路 题解
描述
小苞准备开着车沿着公路自驾。公路上一共有 \(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 ... v_{n-1}\),分别表示站点间的距离。
- 第三行包含 \(n\) 个正整数 \(a_1,a_2 ... a_n\),分别表示在不同站点加油的价格。
输出
输出一行,仅包含一个正整数,表示从站点 \(1\) 开到站点 \(n\),小苞至少要花多少钱加油。
样例
样例输入1
5 4
10 10 10 10
9 8 9 6 5
样例输出1
79
【数据范围】
对于所有测试数据保证:\(1≤n≤10^5, 1≤d≤10^5, 1≤v_i≤10^5, 1≤a_i≤10^5\)。

题解
思路来源:2023CSP-J 普及组第二轮试题及解析( 第二题公路)
虽然想到大概率是贪心或DP,但没做出来是因为没想到以下规律:
对于某个站点 \(i\) ,加油的花费只与 \(min\{a_1,a_2,...a_i\}\) 有关。比如我们在3号站要走到4号站,需要的油可以是从1或2号站点就加好的,也可以是在3号站点加的,就看谁的价格最低。我们逻辑上是在当前站点加油,但实际上是我们在之前那个最便宜的站点不知道加多少油才能遇到下一个更便宜的,索性就先加到能跑到下一站,不够了再事后诸葛亮地修改之前加油的量,然后从1号站点出发时加的油只能从1号站点加(不管贵还是便宜)。所以需要维护一个minCost变量记录最便宜的油价
这是第一个贪心:每次加油只加最便宜的(除非没得选)
第二个贪心是:每次加油只加尽可能少的油。什么叫尽可能少的油呢?比如我在2号站点,油箱里的油只够走1km,但距离下一个站点还有9km,那么我每次加油的时候就只加刚好能让我到下一个站点的油(在这里需要加3升,因为只能加整数升,2L跑8km到不了,只能加3L/12km)。
综合起来,整体的思路如下:
我们从1号站点出发,每到一个站点,先看一下油价是不是更便宜,便宜的话更新 \(minCost\) 。再加油就用这个油价
然后看一下车里的油够不够走到下一个站点,如果够,那就到下一个站点,并且更新还能走的里程数;如果不够,那就需要加油,加到正好能走到下一个站点,并且更新总的油钱。
直到我们走到第 \(n-1\)个站点,处理完第 \(n-1\) 个站点需不需要加油、加多少之后,就可以停止了,下一个站点就是终点站不用再加油了。
代码
#include <bits/stdc++.h>
using namespace std;
int main() {
int n,d,cur=0; // res为最后总的油钱,cur是目前油箱里的油
long long int res = 0;
cin >> n >> d;
int dis[n],price[n+1];
for(int i=1;i<n;i++)
cin >> dis[i];
for(int i=1;i<=n;i++)
cin >> price[i];
int minCost = price[1]; // 当前最便宜的油价
for(int i=1;i<n;i++) {
minCost = min(price[i],minCost); // 更新最便宜的油价
if(cur>=dis[i]) { // 如果油箱剩余的油还能继续走
cur -= dis[i]; // 走到下一个站点
continue; // 不加油,直接跳过循环
}
// 如果油箱剩余的油走不到下一个站点
res += (long long int)(dis[i]-cur+d-1)/d*minCost; // 加油花费
cur += (dis[i]-cur+d-1)/d*d; // 加油之后可以走的里程数
cur -= dis[i]; // 走到下一个站点
}
cout << res << endl;
return 0;
}
浙公网安备 33010602011771号