问题描述:
已知起点与终点的距离为D,油箱的最大油量为Cmax,单位汽油能够支持前进Davg,给定N个加油站的单位油价和离起点的距离(所有加油站都在一条线上),汽车初始时刻处于起点位置,油箱为空,且可以在任意加油站购买任意量的汽油(前提是不超过油箱容量),求从起点到终点的最小花费。如果无法到达终点,则输出能够行驶的最远距离。
输入格式:
第一行输入四个正整数,分别为Cmax(<=100)、D(<=30000)、Davg(<=20)、N(<=500),紧接着N行分别给出N个加油站的单位油价和离起点的距离。
输出格式:
如果能够到达终点,请输出最小花费并保留两位小数。如果不能,请输出"The maximum travel distance = X",X为能够行驶的最远距离,X保留两位小数。
输入样例1:
50 1300 12 8
6.00 1250
7.00 600
7.00 150
7.10 0
7.20 200
7.50 400
7.30 1000
6.85 300
输出样例1:
749.17
输入样例2:
50 1300 12 2
7.10 0
7.00 600
输出样例2:
The maximum travel distance = 1200.00
思路:
- 把终点视为单位油价为0,距离起点为D的加油站,然后将所有加油站按离起点的距离从小到大排列,排序完毕后,如果离起点最近的加油站距离不是0,则表示该汽车无法出发(初始时刻油量为0),输出"The maximum travel distance = 0.00";如果离起点最近的加油站距离为0,则进入下一步
- 假设当前所处的加油站编号为now,接下来将从满油状态下能到达的所有加油站中选出下一个前往的加油站:
(1)寻找距离当前加油站最近的油价低于当前油价的加油站,记为k,加恰好能够到达加油站k的油,然后前往加油站k,即优先前往更低油价的加油站
(2)如果找不到油价低于当前油价的加油站,则寻找油价最低的加油站,在当前加油站加满油,然后前往加油站k,即在没有更低油价的加油站时,前往油价尽可能低的加油站
(3)如果在加满油的状态下找不到能够到达的加油站,则最远能到达的距离为当前加油站的距离加上加满油状态下能够前进的距离,结束算法
(1)的证明:假设三个加油站的顺序为a,b,c(当前在a),且油价大小a>b,则先从a加能够到达b的油,再在b加能够到达c的油,要比直接在a加能够到达c的油划算。因此,在所有能到达的加油站中,总是优先选择最近的油价低于当前油价的加油站。
(2)的证明:假设三个加油站的顺序为a,b,c(当前在a),且油价大小a<b<c,显然应该在a加满油,然后前往b,c中油价更低的b
注意:
- 在距离0处必须要有加油站,否则无法出发
- Cmax、D、Davg、油价、距离都可能是浮点型
代码:
#include<cstdio>
#include<algorithm>
using namespace std;
const int maxn = 510;
const int INF = 1000000000;
struct station {
double price,dis;//价格、与起点的距离
}st[maxn];
bool cmp(station a , station b){
return a.dis < b.dis;
}
int main(){
int n;
double Cmax,D,Davg;
scanf("%lf%lf%lf%d",&Cmax,&D,&Davg,&n);
for(int i = 0 ; i < n ; i++){
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");
}else{
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,计算转移花费
//need为从now到达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;//到达加油站k后油箱内的油为0
}else{
nowTank -= need;
}
}else{
//如果加油站k的油价高于当前油价,那么就要加满油
ans += (Cmax - nowTank) * st[now].price;
//到达加油站k之后油量
nowTank = Cmax -need;
}
now = k;//到达加油站k,进入下一循环
}
if(now == n)
printf("%.2f\n",ans);
else
printf("The maximum travel distance = %.2f\n",st[now].dis+MAX);
}
return 0;
}