1033. To Fill or Not to Fill (25) 解析总结
题目描述
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.
输入
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.
输出
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.
样例输入
59 525 19 2
3.00 314
3.00 0
样例输出
82.89
-
贪心:
-
当前最优,子结构最优,自顶向下,迭代,缩小范围
问题的最优子结构性质是该问题可用贪心算法或动态规划算法求解的关键特征。贪心算法的每一次操作都对结果产生直接影响,而动态规划则不是。
贪心算法对每个子问题的解决方案都做出选择,不能回退;动态规划则会根据以前的选择结果对当前进行选择,有回退功能。动态规划主要运用于二维或三维问题,而贪心一般是一维问题
解题思路:
-
若没有到达终点,重复以下过程,一直走,不断缩小范围,直到到达
1. 在当前站加满所能走的最大距离(Cmax*Davg)内有加油站
1.1 有比当前站的油价小的第一个加油站,加到能到达这个站的油即可(注:一定是第一个当前小的,关于为什么不妨选择后面最小的比比看)
1.2 没有比当前价格小的,应该在当前站点加满油,找到该范围内除当前油站的最小者,去该加油站。
2. 每次到达一个站点后,刷新油量(遇到1.2情况时会有剩余油量的)
1.2 最大距离内没有加油站,加满,跑最大距离即可
已经到达,结束旅程,输出花费贪心主要原则是不断局部采取最优策略,不断缩小范围,从而达到整体最优
代码
#include<cstdio>
#include<algorithm>
using namespace std;
const int maxn = 510;
struct Station{
double d;
double price;
};
bool cmp(Station a,Station b){
return a.d<b.d;
};
int main(){
Station station[maxn];
double Cmax,D,Davg;
int N;
scanf("%lf%lf%lf%d",&Cmax,&D,&Davg,&N);
for(int i=0;i<N;i++){
scanf("%lf%lf",&station[i].price,&station[i].d);
}
station[N].price=0;
station[N].d=D;//为了方便计算,把目的地也加入
sort(station,station+N+1,cmp);
bool isReach=false;//是否到达目的地
double maxD = Cmax*Davg;//加满可以跑的最远距离
int curStation=0;//当前到达的加油站,初始置为第一个
double totalCost = 0;//总花费
double curC=0;//当前油量
// 极限条件
if(station[0].d!=0){//出发地没有加油站,不能到达
printf("The maxinum travel distance = 0.00");
}
if(D==0){ //目标地点为0 说明不用走了 现在就在目的地了
isReach = true;
}
while(!isReach){
double curSmallPrice =1000000;//用来记录后面最小费用加油站的费用
int curSmallStation = curStation;//最小费用加油站的坐标位置
bool isGas=false;//范围内是否有加油站
bool isCheaper=false;//范围内是否有比当前站便宜的
//用for循坏来寻找从当前加油站最大行驶范围内的加油站情况
for(int i=curStation+1;i<=N;i++){
if(station[i].d-station[curStation].d<=maxD){
isGas=true;
if(station[i].price<station[curStation].price){
curSmallStation=i;
isCheaper = true;
break;
}else{//1.1.2
if(station[i].price<curSmallPrice){
curSmallPrice = station[i].price;
curSmallStation=i;//记录范围内最便宜的
}
}
}else{
break;
}
}
//当前站后面的加油站统计完毕,计算费用
if(isGas){
double d = station[curSmallStation].d-station[curStation].d;//合适费用的加油站与当前加油站的距离
double needGas;
if(isCheaper){//有比当前站便宜的
needGas=d/Davg-curC; //上一次行程中可能会有剩余,所以当前需要加的油减去剩余的油
curC = 0;//到达站点后,正好用完油
}else{//没有比当前站点便宜的,在当前站点加满油,开往后面站点最便宜的加油站
needGas = Cmax-curC;//加满
curC = Cmax - d/Davg;//到达那个便宜的站点,刷新剩余油量
}
totalCost+=needGas*station[curStation].price;//计算此次费用
curStation=curSmallStation;//刷新当前站点
}else if(!isReach){//范围内没有加油站了,直接加满跑出最大距离
printf("The maxinum travel distance = %.2lf\n",station[curStation].d+maxD);
return 0;
}
if(station[curStation].d-station[N].d==0){//当前站与目标站距离为0,说明到站了
isReach=true;
}
}
//输出费用
printf("%.2lf\n",totalCost);
return 0;
}