1033 To Fill or Not to Fill (25 分)
题意
已知起点与终点的距离为D,油箱的最大油量为Cmax,单位汽油能够支持前进Davg。给定N个加油站的单位油价和离起点的距离(所有加油站都在一条线上),汽车初始时刻处于起点位置,油箱为空,且可以在任意加油站购买任意量的汽油(前提是不超过油箱容量),求从起点到终点的最小花费。如果无法到达终点,则输出能够行驶的最远距离。
思路
- 把终点视为单位油价为0、离起点距离为D的加油站,然后将所有加油站按离起点的距离从小到大进行排序。排序完毕后,如果离起点最近的加油站的距离不是0,则表示汽车无法出发(初始时刻油量为0),输出“The maximum travel distance = 0.00”;如果离起点最近的加油站的距离是0(即加油站就在起点),
- 假设当前所处的加油站编号为now,接下来将从满油状态下能到达的所有加油站中选出下一个前往的加油站,策略如下:
- 寻找距离当前加油站最近的油价低于当前油价的加油站(记为k),加恰好能够到达加油站k的油,然后前往加油站k(即优先前往更低油价的加油站)。
- 如果找不到油价低于当前油价的加油站,则寻找油价最低的加油站,在当前加油站加满油,然后前往加油站k(即在没有更低油价的加油站时,前往油价尽可能低的加油站)。
- 如果在满油状态下都找不到能到达的加油站,则最远能到达的距离为当前加油站的距离加上满油状态下能前进的距离,结束算法(即没有加油站可以到达时结束算法)。
上面的策略当满足条件③、或者到达加油站n(即终点)时结束。其中①和②的证明如下。
策略①的证明:假设三个加油站的顺序为a、b、c (当前在加油站a),且油价大小为a>b(与c的油价大小无关),则先从a加能到达b的油,然后在b加能到达c的油,要比直接从a加能到达c的油要节省(因为a的油价比b高)。因此,在所有能到达的加油站中,总是优先选择最近的油价低于当前油价的加油站。
策略②的证明:假设三个加油站的顺序为a、b、c (当前在加油站a),且油价大小为a<b<c,显然应该先在a加满油(因为b、c油价高),然后前往b、c中油价较低的加油站b(如果一定要去c,也应该是先到油价相对便宜的b,然后去c才更划算(从c出发买油价格高,还不如在b先买好))。
代码
const int N=510;
struct Node
{
double price;
int dist;
bool operator<(const Node &W) const
{
return dist < W.dist;
}
}a[N];
int Cmax,D,Davg;
int n;
int main()
{
cin>>Cmax>>D>>Davg>>n;
for(int i=0;i<n;i++) cin>>a[i].price>>a[i].dist;
sort(a,a+n);
a[n]={0,D};
if(a[0].dist != 0)
cout<<"The maximum travel distance = 0.00"<<endl;
else
{
double maxdist=Cmax*Davg;
double remain_oil=0;//当前汽车剩余油量
double res=0;
for(int i=0;i<n;)//i表示需要进行加油的站的下标
{
int k=-1;
int j=i+1;
while(j<=n && a[j].dist <= maxdist)
{
if(a[j].price < a[i].price)
{
k=j;
break;
}
else if(k==-1 || a[j].price < a[k].price)
k=j;
j++;
}
if(k == -1) break;
double need=(double)(a[k].dist-a[i].dist)/Davg;
if(a[k].price < a[i].price)
{
res+=(need-remain_oil)*a[i].price;
remain_oil=0;
}
else
{
res+=(Cmax-remain_oil)*a[i].price;
remain_oil=Cmax-need;
}
//cout<<"---"<<k<<' '<<maxdist<<' '<<remain_oil<<' '<<res<<endl;
maxdist+=a[k].dist-a[i].dist;
i=k;
}
if(maxdist < D) printf("The maximum travel distance = %.2f\n",maxdist);
else printf("%.2f\n",res);
}
//system("pause");
return 0;
}