You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.
### [$P9749$ [$CSP-J$ $2023$] 公路](https://www.luogu.com.cn/problem/P9749?contestId=140858)
### 题目分析
题目较长,需要反复阅读才能理解题意,但解题的关键也在于理解题意。
简单描述题目为:开车从$1$ 走到 $n$, 路过站点时可以加油,每个站点的油价不同,有高有低,如何加油可以使 ** 总费用最低**?
考虑最开始的情况:在起始位置,车辆一滴油都没有,所以在 $a[1]$ 站点必须要加油。那么加多少呢?
很自然根据贪心思想,找到下一个站点 $a[j]$, 如果 $a[j]$ 比 $a[1]$ 价格便宜,就可以在 $a[j]$ 加油。
因此,程序的主体算法可以用伪代码表示为:
```
while (车辆还没有跑到终点) {
找到下一个便宜的加油站 a[j]
加的油刚好能到站点 a[j]
累加费用
}
输出总费用
```
可以简单证明一下贪心算法的合理性:
- 从第一站开始,只有一种最优的加油方案,就是加最少的油跑到下一个便宜的加油站。
- 以此类推,跑完全程,每一次加油都是最便宜的方案,因此总方案也是最便宜的。
#### 实现细节
从节点 $[i]$ 跑到节点 $[j]$ 油箱里的油 ** 可能是有剩余的**,编码时需要把剩余的油量考虑在内。有点类似计算加减法的进位和借位。
计算从 $i$ 到 $j$ 的费用可以表示为:
```
int 计算费用(int i, int j) {
1. 计算 i -> j 的路程
if (油箱里剩余的油足够) {
不用加油,油箱里的油减少
费用为 0
} else {
需要加油,计算加多少油,需要考虑油箱剩余的油
计算加油费用
跑到 j 时,油箱的油还能跑几公里,保存到全局变量
}
返回费用
}
```
```cpp {.line-numbers}
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 100010;
int v[N]; // 站点间的距离
int a[N]; // 每个站点的油价
int n; // n个站点
double d; // 1升油可以跑多少公里
int money; // 一共花费
int r; // 油箱的油可以跑的公里数
int getMoney(int begin, int end) { // 左闭右开
int length = 0;
for (int i = begin; i < end ; i ++) length += v [ i ];
if ( r > = length) {
// 不用加油
r -= length;
return 0;
} else {
// 需要加油, 计算加多少油: 油箱的油还有剩余,还能跑几公里
int add = ceil((length - r) / d);
// 计算加油费用
int x = a[begin] * add;
// 跑到 end, 油箱的油还能跑几公里
r = add * d + r - length;
return x;
}
}
signed main() {
cin >> n >> d; // n个站点,每升油可以让车前进d公里
for (int i = 1; i < n ; i ++) cin > > v[i]; // 站点间的距离 a1~a2,a2~a3,...
for (int i = 1; i < = n; i++) cin >> a[i]; // 不同站点加油的价格
int i = 1;
while (i < n ) {
int j = i + 1 ;
// 找到下一个便宜的加油站
while ( j <= n && a [ j ] > = a[i]) j++;
money += getMoney(i, j);
i = j;
}
cout < < money << endl ;
}
```
3 总结
本题的题目具有一定的迷惑性,具体模拟一遍开车加油的流程就可以发现,最优化的加油方案只有一种,就是每一步都找到花费最小的加油站,然后开车过去即可。
因此,贪心加模拟可以解决此问题。