1033 To Fill or Not to Fill (25 分)
题目传送门:1033 To Fill or Not to Fill (25 分)
一、题目大意
求从杭州站到目的站的最小油费。
杭州站与目的站之间有很多个加油站,每个加油站的油价不同。如果无法到达输出最大行驶距离,否则输出到达目的站的最小油费。
二、解题思路
这道题很明显是贪心算法,但是具体怎么求下一站点,还挺复杂的。
注意:题目中说所有的站点的坐标都是小于等于终点站的,那么我们就不用担心在中途会达到终点站,终点站一定是在最后一站后面。
先根据距离排序,如果第一个点的坐标不是0则无法加油出发,不可到达终点,直接输出0即可。
然后在while循环当前点开始分类贪心讨论:
- 如果从当前站点加满油的可达范围内,没有站点
1.1 如果可以加满油的可达范围内到达终点,油不够则加油,结束循环
1.2 无法到达终点,则直接输出最大可达距离,然后结束循环。 - 如果从当前站点加满油的可达范围内,有站点
2.1 如果可达范围内有比当前站更便宜的站,先去最近的更便宜的站。
2.2 如果可达范围内没有比当前站更便宜的站,则先看是否可以直接到达终点。如果无法到达终点,则把油加满,去往可达范围内最便宜的站。
这道题困惑了我好几天了,今天思路终于通透了,之前一直想不明白的是,如果到了没找到比当前便宜的加油站,而加满油去可达范围内最近的加油站,那去下一站时,下一站更便宜而原本又装了满油没用完,岂不是很浪费钱,感觉不符合贪心思想。但今天我终于想通了,就是满油到达最近的加油站后,是无法靠之前的油到更便宜下一站的,因为我是在可达范围内无更便宜加油站前提下进行的,所以我在此站装满油后,到了最便宜的站后,就是还剩了油, 也无法到达下一个更便宜的加油站。所以是符合贪心规则的。
还有一个疑问,就是会不会到达终点站的时候,还有剩余的油,如果有剩余的油,那岂不是就不是最优解了吗。不过这个问题在我写完这句话的时候,突然想通了,就是在上面分析2.1中做的处理逻辑,先判断是否可以到达终点,如果不可以到达终点,再加满油去最近的站点。
三、AC代码
#include<bits/stdc++.h>
using namespace std;
template<typename T = int>
T read(){
T x;
cin >> x;
return x;
}
struct Station
{
double price;
int position;
};
int main(){
double C = read<double>(), D = read<double>(), AVG = read<double>();
int N = read();
vector<Station>v(N);
for(int i = 0; i < N; i++){
v[i].price = read<double>();
v[i].position = read<double>();
}
sort(v.begin(), v.end(), [](Station a, Station b)->bool{
return a.position < b.position;
});
if(v[0].position > 0){
cout << "The maximum travel distance = 0.00" << endl;
return 0;
}
// for(auto i: v){
// cerr << "jal::" << i.position << ' ' << i.price << endl;
// }
double fullDis = C * AVG;
// cout << "fullDis = " << fullDis << endl;
int curIndex = 0;
double curPos, curPrice, sumPrice = 0, curCap = 0;
while(curIndex < v.size()){
curPos = v[curIndex].position, curPrice = v[curIndex].price;
// cerr << "curIndex = " << curIndex << endl << endl;
int hasStation = -1, cheapestPos = -1, cheapestPos2 = -1;
for(int i = curIndex+1; i < v.size(); i++){
if(v[i].position > curPos + fullDis)break;
hasStation = 1;// 范围内可达
cheapestPos2 = i;
if(v[i].price < curPrice){
cheapestPos = i;
break;// 最近的更便宜的站
}
}
// cerr << "cheapestPos = "<< cheapestPos << " cheapestPos2 = " << cheapestPos2 << endl;
if(hasStation == 1){// 1. 范围内有可达站点
if(cheapestPos != -1){// 1.1 有最近的便宜站点
if(curCap * AVG >= v[cheapestPos].position - curPos){// 当前的油量可以直接到达
curCap -= (v[cheapestPos].position - curPos)/AVG;
}else{// 需要加油才能到达
sumPrice += ((v[cheapestPos].position - curPos) / AVG - curCap) * curPrice;
curCap = 0;
}
curIndex = cheapestPos;
}else{ // 2. 范围内的站点都比当前点贵,先看是否可以直接到达终点,否则把油加满开到最便宜的点。
if(curPos + fullDis >= D){// 可以直接到达终点
if(curCap * AVG <= D - curPos){// 2.1.1 油不足够跑到终点,先加油
sumPrice += ((D - curPos) / AVG - curCap) * curPrice;
curCap = 0;
}else{
curCap -= (D - curPos) / AVG;
}
break;
}
for(int i = curIndex+1; i < v.size(); i++){
if(v[i].position > curPos + fullDis)break;
if(v[i].price < v[cheapestPos2].price){
cheapestPos2 = i;
}
}
sumPrice += (C - curCap) * curPrice;
curCap = C - (v[cheapestPos2].position - curPos) / AVG;
curIndex = cheapestPos2;
}
}else{// 2. 范围内无可达站点
if(curPos + fullDis >= D){// 2.1 可以直接到目的地
if(curCap*AVG <= D - curPos){// 2.1.1 油不足够跑到终点,先加油
sumPrice += ((D - curPos) / AVG - curCap) * curPrice;
curCap = 0;
}else{
curCap -= (D - curPos)/AVG;
}
break;
}else{
cout << "The maximum travel distance = "<<fixed << setprecision(2) << curPos + fullDis << endl;
return 0;
}
}
}
// cout << "curCap = " << curCap << endl;
cout <<fixed << setprecision(2) << sumPrice << endl;
}