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.
3.4 KiB
3.4 KiB
P9749
[CSP-J
2023
] 公路
题目分析
题目较长,需要反复阅读才能理解题意,但解题的关键也在于理解题意。
简单描述题目为:开车从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 时,油箱的油还能跑几公里,保存到全局变量
}
返回费用
}
#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 总结 本题的题目具有一定的迷惑性,具体模拟一遍开车加油的流程就可以发现,最优化的加油方案只有一种,就是每一步都找到花费最小的加油站,然后开车过去即可。
因此,贪心加模拟可以解决此问题。