1033 To Fill or Not to Fill (25分)
题目链接:PAT A 1033
题目大意:输入第一行给出四个数,分别是油箱最大容量,杭州到目的地的距离,每单位的油能让汽车跑多远以及加油站的数目。默认初始时汽车的油箱是空的,在任意加油站可以加任意数目的油(不能超过汽车油箱最大容量),要求输出从杭州到目的地所需要油价最少的钱数,如果无法到达目的地,则输出能够行驶的最大距离。
思路分析:有难度的一道题。主要是要用到贪心算法,即在对问题求解时,总是做出在当前看来是最好的选择。也就是说,不从整体最优上加以考虑,他所做出的是在某种意义上的局部最优解,最后所有局部最优解共同构成整体最优解。对于这道题来说,我们就需要在当前加油站所能到达的所有加油站中,选择一个油价最低的加油站,这就有两种可能:一是这个加油站的油价低于当前加油站,那么我们只需要加从当前加油站恰好能到达这个加油站距离的油;二是这个加油站的油价高于当前加油站,那么我们需要在油箱中加满油,这就是这道题的核心思想,所以我们可以分几个步骤解决:
步骤一:把终点看为是距离为d,价格为0的加油站,和其他加油站一同存储在结构体数组中,之后按照距离大小对结构体数组进行排序。需要注意的是,如果距离最小加油站的距离不是0,那么需要直接输出这个距离,因为初始时油箱是空的,所以第一个加油站距离必须是0汽车才能发动。
步骤二:贪心算法核心,即在能到达的加油站中选择一个油价最低的加油站,如果这个加油站油价低于当前加油站,则加恰好到达这个加油站的油,如果这个加油站油价高于当前加油站,则加满油。之后循环重复步骤二,直到到达终点或者没有能到达的加油站,这时最大距离就是起点到达这个加油站的距离加上汽车加满油后能行驶的最大距离。
注意:题目中只说输入的数是正数和非负数,并没有说一定是整数(虽然样例中给出的是整数),因此对于油箱最大容量,距离等变量需要设置成浮点数。
AC代码:
#include<iostream>
#include<algorithm>
#include<vector>
using namespace std;
struct gas{
double price, distance;
};
bool cmp(gas a, gas b) {
return a.distance < b.distance;
}
const int INF = 0x7fffffff; //一个很大的数
int main() {
int n;
double cmax, d, davg;
scanf("%lf %lf %lf %d", &cmax, &d, &davg, &n);
vector<gas> v(n + 1);
for(int i = 0; i < n; i++)
scanf("%lf %lf", &v[i].price, &v[i].distance);
v[n].price = 0; //终点油价为0
v[n].distance = d; //终点距离
sort(v.begin(), v.end(), cmp);
if(v[0].distance != 0) //起点加油站距离不为0
printf("The maximum travel distance = 0.00");
else {
int now = 0; //now是当前加油站编号,初始时为0
double ans = 0, tank = 0, maxdis = cmax * davg; //ans是要求的价格,tank是现在油箱里有多少油,初始时为0,maxdis是加满油能走的最大距离
while(now < n) {
int k = -1; //变量k记录从当前加油站能到达的加油站中油价最低的加油站
double money = INF; //记录从当前加油站能到达的加油站中油价最低的加油站的油价
for(int i = now + 1; i <= n && v[i].distance - v[now].distance <= maxdis; i++) {
if(v[i].price < money) { //如果油价更低,则更新
money = v[i].price;
k = i;
}
if(v[i].price < v[now].price) //找到一个价格低于当前加油站的加油站,直接退出for循环
break;
}
if(k == -1) //说明到达不了终点了,退出while循环
break;
double need = (v[k].distance - v[now].distance) / davg; //到达下一个加油站需要的油量
if(v[k].price < v[now].price) { //如果下个加油站油价低于当前加油站,只加能到达下个加油站所需的油
ans += (need - tank) * v[now].price; //更新价格
tank = 0; //到站后油箱内油量为0
}
else { //能到达的加油站油价都比当前加油站高,则在当前加油站加满油
ans += (cmax - tank) * v[now].price; //更新价格
tank = cmax - need; //到站后油箱内油量为最大油量减去需要的油量
}
now = k; //更新到达的加油站编号
}
if(now == n) //说明能到终点
printf("%.2f", ans);
else //到不了终点
printf("The maximum travel distance = %.2f", v[now].distance + maxdis);
}
return 0;
}