题目大意:
已知起点与终点的距离为D,油箱的最大油量为Cmax,单位汽油能够支持前进Davg。
给定N个加油站的单位油价和离起点的距离(所有加油站都在一条线上)汽车初始时刻位于起点位置,邮箱为空,
可以在任意加油站购买任意数量的汽油(不超过邮箱储量)。求从起点到终点的最小花费,如果无法到达终点,则输出能够行驶的最远距离。
思路:
1.把终点视为单位油价为0,离起点距离为D的加油站,然后将所有的加油站按离起点的距离从小到大进行排序。
排序完毕后,如果离起点最近的加油站距离不是0,则表示汽车无法出发(初始时刻油量为0),输出:The maximum travel distance = 0.00
如果离起点最近的加油站距离是0(即起点就是加油站),则进入步骤2
2.假设当前所处的加油站编号为now,接下来将从满油状态下能到达的所有加油站中选出下一个前往的加油站,策略如下:
1)寻找距离当前加油站最近的油价低于当前油价的加油站(记为k),加恰好能够到达加油站k的油,然后前往加油站k(优先前往油价最低的加油站)
2)如果找不到油价低于当前油价的加油站,则寻找油价最低的加油站, 在当前加油站加满油,然后前往加油站k,(在没有更低油价的加油站时,前往油价尽可能低的加油站)
3)如果在加满油的状态下都找不见能够到达的加油站,则最远到达距离为当前加油站的距离加上满油状态下能前进的距离,结束算法。
实际实现时,策略1与策略2寻找加油站的过程可以结合在一起,即在所有满油状态下能到达的加油站中,选出油价最低的那个加油站,而一旦在枚举过程中找见油价低于当前油价的加油站,即退出循环。
Cmax,D,Davg,油价,距离都是浮点型,不能设置成为int型。
贪心策略证明:
1.假设三个加油站的顺序为a,b,c,且油价大小为a>b,则先从a加能到b的油,然后从b加能够到c的油,一定比从a加能到c的油节省。
因此在所有的加油站中,总是优先选择最近的油价低于当前加油站的油价的加油站。
2.假设三个加油站的顺序为a,b,c,当前加油站为a,且油价大小为a<b<c,显然应该在a加满油,然后前往bc中油价较低的加油站。
如果一定要去c,也应该是先到油价相对便宜的b,然后再去c更为划算。
AC代码:
//PAT_A 1033
#include<cstdio>
#include<algorithm>
using namespace std;
const int maxn = 510;
const int INF = 100000000;
struct station {
double price, dis;//price表示价格,dis表示到起点的距离
}st[maxn];
bool cmp(station a, station b) {
return a.dis < b.dis;
}
int main() {
int n;
double Cmax, D, Davg;
//Cmax:油箱最大油量,D:与终点距离,Davg:单位汽油能够支持的前景距离,n:加油站个数
(void)scanf("%lf %lf %lf %d", &Cmax, &D, &Davg, &n);
for (int i = 0; i < n; i++) {
(void)scanf("%lf %lf", &st[i].price, &st[i].dis);
}
st[n].price = 0;
st[n].dis = D;//最后一个加油站
sort(st, st + n, cmp);//按距离排序
if (st[0].dis != 0) {//第一个加油站不在起点
printf("The maximum travel distance = 0.00\n");
return 0;
}
int now = 0;//当前所处加油站编号
double ans = 0, nowTank = 0, Max = Cmax * Davg;//总花费,当前邮箱剩余油量,加满油的行驶距离
while (now < n) {
int k = -1;//最低油价加油站编号
double priceMin = INF;
for (int i = now + 1; i <= n && st[i].dis - st[now].dis <= Max; i++) {
if (st[i].price < priceMin) {
priceMin = st[i].price;
k = i;
if (priceMin < st[now].price)break;//如果找见比当前加油站便宜的加油站,中断循环
}
}
if (k == -1) break;//满油状态下无法找到加油站,退出循环结果
//找见可到达的加油站k,计算花费
double need = (st[k].dis - st[now].dis) / Davg;
if (priceMin < st[now].price) {//如果加油站k的油价低于当前油价,只买足够到加油站k的油
if (nowTank < need) {
ans += (need - nowTank) * st[now].price;
nowTank = 0;//到达之后
}
else {
nowTank -= need;//直接到达k
}
}
else {//没有加油站的价格低于当前油价,直接加满,到k
ans += (Cmax - nowTank) * st[now].price;
nowTank = Cmax - need;//到达之后
}
now = k;
}
if (now == n) {//到达终点
printf("%.2f\n", ans);
}
else {
printf("The maximum travel distance = %.2f", st[now].dis + Max);
}
return 0;
}