2031: To Fill or Not to Fill
时间限制: 1 Sec 内存限制: 32 MB
提交: 599 解决: 132
With highways available, driving a car from Hangzhou to any other city is easy. But since the tank capacity of a car is limited, we have to find gas stations on the way from time to time. Different gas station may give different price. You are asked to carefully design the cheapest route to go.
Input Specification:
Each input file contains one test case. For each case, the first line contains 4 positive numbers: Cmax (<= 100), the maximum capacity of the tank; D (<=30000), the distance between Hangzhou and the destination city; Davg (<=20), the average distance per unit gas that the car can run; and N (<= 500), the total number of gas stations. Then N lines follow, each contains a pair of non-negative numbers: Pi, the unit gas price, and Di (<=D), the distance between this station and Hangzhou, for i=1,...N. All the numbers in a line are separated by a space.
Output Specification:
For each test case, print the cheapest price in a line, accurate up to 2 decimal places. It is assumed that the tank is empty at the beginning. If it is impossible to reach the destination, print "The maximum travel distance = X" where X is the maximum possible distance the car can run, accurate up to 2 decimal places.
Sample Input 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
Sample Output 1:
749.17
Sample Input 2:
50 1300 12 2
7.10 0
7.00 600
Sample Output 2:
The maximum travel distance = 1200.00
题意就是从起点到终点有很多的加油站 每个加油站有距离起点的距离和单价 我们从起点开车到终点 开始油量为0 要尽可能走到终点 问所求的最小花费是多少?如果走不到终点 就输出最远距离。
一开始给出油箱大小,到终点的距离,还有每单价油可以走的里程数,以及加油站的信息
分析:
由于这个问题逻辑比较复杂 可以先研究样例
假设汽车从起点S到终点E必须通过加一次油到达 可达范围内有多个价格不一的加油站
如果可达范围内有更便宜的G 我们要选择那个加油站续命 先加够到这个加油站G的油 在到G处加新的油 这样就相当于从起点到G再到终点的松弛 可以通过这个加油站把后面的路程的花费给减小“松弛”掉
如果选择某个并不是选择可达范围内最靠前的更便宜的加油站去加油站
如果选择了一个较远的,花费更大的站A 那么就必然导致起点加油变多花费变多,从A站新加的油到终点的这段距离花费变多 虽然路程变小 从全局来看 两种不同的选择花费不同的路段里 第二段从G到A花费变多 从A到终点E花费也变多 从而导致全局花费更大
如果选择了一个较近的花费更大的站B 那么从起点S到B花费不变 那么从B到G花费变多 从G到终点花费也变多 从而导致全局更大的花费
所以在当前站 选择可达范围内最近的且更便宜的能够带来更小的局部最优 从而获得更小的全局花费
如果没有更便宜的
那么就要在初始S加满 这样才能尽可能少加更贵的油 从而减小花费
那么选择所有比初始S贵的里面最便宜G的 那么从S到G花费起点S的油钱 从G到终点E 再化一部分新加的G站油钱 如果不是这样选 得到的花费只会更大 怎么呢?
我们来看 这种情况下 续命油的油量是确定的 因为我前面加满了
如果选择一个更近的站A但并不是可选加油站中最便宜的 那么从起点S到A花费不变,从A到G花费不变 但到终点需要加一次油 这时需要加的油会更贵一点(A站油钱) 从而全局花费更大
如果选择一个更远的站B但并不是可选加油站中最便宜的 那么起点到G花费不变 G到B花费不变 B到终点E 需要续一次油 花费变大(B站油钱)
所以在这种 分支下 我们也要选择贵中最便宜的那个去加油 从而获得全局最优
我们要找的续命的加油站 必须是可达范围里最便宜的 只有这样 才能通过这个最便宜的加油站 获取尽可能便宜的局部最优花费 这样再去选
你有可能会有疑问
如果我们想G走着走着发现一个可达范围内更便宜站P的怎么办 即便如此 我们也要先到G站 看看能不能不加油直接走到P站 不过不可能! 因为P是G站决策之前的可达范围之外的站 必然不可能不加油走到P站 所以我们有必要中间续油 那么续油的话 只能在G续油 因为G是最相对便宜的 我们只有先到了G再看看需要续多少油到更便宜的加油站去 如果没有更便宜的加油站 则继续选择可达范围内所有更贵的里面最便宜的 所以整个过程就是
局部最优+局部最优+...+局部最优 = 全局最优
于是这个问题的逻辑大致就是如此 对每个加油站执行相同的逻辑 我们发现这样的话就是贪心策略 每到一个加油站 在能到达的范围内选择局部最优策略 这样走下来就能得到全局最优策略 如果在加油站不进行局部最优的策略 那么就必然导致花费变大
如果起始点没有加油站 那么就输出0 因为加不到油必然走不出去
否则:
看看加满油的话 能到达的范围内 有没有加油站
如果没有 那么就加满油尽可能跑 能跑多远就输出多远
如果有加油站 看看有没有一个加油站是他能到达的范围内且离当前点比较近的?我们设此站位G站
如果有 我们就买刚好到G站的油量:因为到了G站买油可以得到更便宜的价格 从而尽可能的得到更小的花费 如果没有 全都是比当前站更贵的加油站 那么就要从贵中选一个最便宜的设为H站 我们要走到那里继续买油才能尽可能的减小花费 注意 这时要从当前站加满油 因为H站的油是更贵的 但是又不能因为油贵而不走 还是要尽可能往前走的 这时如果不从H站买 从其他站买 只会导致更多的花费 所以我们要尽可能少的从H站买油 我们到了H站要站在H站的位置考虑更优的花费时 再从H站买油 故从当前站要加满油 然后去向H站
由于本题需要维护的参数比较多 逻辑比较复杂 还是要想清楚逻辑后 把变量写的可读性高点 才能少出bug
时间复杂度:O(n^(所能到达的范围中的加油站个数))<O(n^2)<1000ms
#include<cstdio>
#include<cstring>
#include<iostream>
#include<map>
#include<cmath>
#include<algorithm>
#include<set>
#include<vector>
#include<climits>
#define Equal(a,b) (fabs((a)-(b))<=eps)
#define LessEqu(a,b) (((a)-(b))<=eps)
#define Lessthan(a,b) (((a)-(b))<-eps)
#define Morethan(a,b) (((a)-(b))>=eps)
#define MoreEqu(a,b) (((a)-(b))>=-eps)
const double eps = 1e-5;
using namespace std;
typedef long long ll;
struct station{double cost;double dis;
}S[510];
bool cmp(station a,station b){return Lessthan(a.dis,b.dis);
}
int main(){double c,dis,avg;int sta;ios::sync_with_stdio(0);cin>>c>>dis>>avg>>sta;for(int i=1;i<=sta;i++){cin>>S[i].cost>>S[i].dis;}double Lowest = S[1].cost;for(int i=2;i<=sta;i++){if(Lessthan(S[i].cost,Lowest)){Lowest = S[i].cost;}}sort(S+1,S+1+sta,cmp);if(!Equal(S[1].dis,0.0)){cout<<"The maximum travel distance = 0.00"<<endl;return 0;}bool f=0;S[sta+1].cost = 0,S[sta+1].dis = dis;double exp=0,canadd=c,distans=0,cut=0,unit=c;double costoil,oil = 0;bool fal = 0;for(int i=1;i<sta+1;i++){oil = (distans-S[i].dis)/avg;double Maxlim = S[i].dis + c*avg;double CMP = S[i].cost;int j,u=i;for(j=i+1;j<=sta+1&&LessEqu(S[j].dis,Maxlim);j++){if(Lessthan(S[j].cost,CMP)){CMP = S[j].cost;u = j;break;}}if(u==i&&j==i+1){//后面没加油站distans = Maxlim;fal = 1;break;}if(u!=i){//有个更便宜的unit = (S[u].dis-distans)/avg;//减去的是什么很关键exp+=S[i].cost*unit;// canadd = unit;distans = S[u].dis;i = u-1;oil += unit;costoil = unit;continue;}//如果后面的都是比自己贵的CMP = S[i+1].cost;u = i+1;for(int j=i+2;j<=sta+1&&LessEqu(S[j].dis,Maxlim);j++){if(LessEqu(S[j].cost,CMP)){//尽可能少买 也就是尽可能远CMP = S[j].cost;u = j;}}canadd = c-oil;distans += (canadd)*avg;exp+=canadd*S[i].cost;costoil = (S[u].dis-S[i].dis)/avg;oil=c;i = u-1;}if(fal)printf("The maximum travel distance = %.2f\n",distans);else printf("%.2f\n",round(exp*100)/100);return 0;
}