题解:P1016 [NOIP 1999 普及组/提高组] 旅行家的预算
S_Miscedence · · 题解
P1016 旅行家的预算 题解
题目大意
你要从起点开到终点,沿途有若干加油站,每个站油价不同。汽车油箱有容量上限,只能在加油站加油。你希望,如果你能到达终点,所花的钱能最少。
前言
面对题目中有最小或最大的字眼,一般都会考虑贪心,二分等算法。读完题后,我们发现这其实更像一道贪心题,只要保证我能买到的油都是当前状态下最便宜的油,那么如果我能到达终点,钱一定花的最少。对于当前状态,我们作如下解释,顺带解释贪心的正确性。
详细分析及贪心证明
很显而易见的是,我们在每一个加油站选择加油只会有两种情况:
- 如果当前的油能到达范围内某个更便宜的车站,那么就刚好加油加到正好能到达那个便宜加油站。
- 如果当前的油钱能开到的范围内,不存在更便宜的加油站,那么就在这一站把油加满,再开到最便宜的加油站。
考虑一下这为什么是对的?只要前面有更便宜的,绝不多买当前贵油;没有更便宜时,当前就是最低价,加满最划算,我们做到了每一步都是局部最优。那么后面就是代码实现的问题了,细节较多,具体看代码:
#include<bits/stdc++.h>
using namespace std;
const int N=10;
struct node{
double d,p;//d为距起点距离,p为油价
};
node a[N];
double s,c,l,p0;//具体含义见题意
int n;
bool cmp(node x,node y){
return x.d<y.d;//按加油站距离由近到远排序
}
int main(){
cin>>s>>c>>l>>p0>>n;
if(s==0){//总路程为0,起点就是终点无需行驶
cout<<"0.00"<<endl;//花费为0,保留两位小数输出
return 0;//直接结束程序
}
a[0].d=0; a[0].p=p0;//把起点当作第0号加油站,距离0,油价为起点油价
for(int i=1;i<=n;i++){
cin>>a[i].d>>a[i].p;
}
n++;
a[n].d=s; a[n].p=0;//虚拟终点,距离为总路程,油价设为0
sort(a,a+n+1,cmp);//将所有加油站按距离从小到大排序
double dis=c*l;//计算油箱加满后能行驶的最远距离
int flag=1;//可达标记,1代表可以到达终点,0代表无法到达
for(int i=0;i<n;i++){//遍历每两个相邻加油站
if(a[i+1].d-a[i].d>dis){//相邻站点距离超过最远距离
flag=0;
break;//标记不可达,跳出循环
}
}
if(!flag){//判断如果整体不可达
cout<<"No Solution"<<endl;//输出无解
return 0;//结束程序
}
double oil=0,cost=0;//记录当前油箱剩余油量,总加油花费
int pos=0;//记录当前所处加油站编号,初始在起点
while(pos<n){//没到终点就一直行驶
int firstpos=-1;//记录前方第一个比当前油价便宜的加油站,初始-1未找到
for(int i=pos+1;i<=n;i++){//遍历当前能到达范围内的所有加油站
if(a[i].d-a[pos].d>dis) break;//超出续航范围,停止查找
if(a[i].p<a[pos].p){//找到油价更低的加油站
firstpos=i;
break;//记录编号并退出查找
}
}
if(firstpos!=-1){//存在更近且更便宜的加油站
double oilto=(a[firstpos].d-a[pos].d)/l;//计算前往需油量
if(oil<oilto){//当前油量不足以开到便宜加油站
cost+=(oilto-oil)*a[pos].p;//加足够的油,累加花费
oil=oilto;//更新油箱油量为刚好够用
}
oil-=oilto;//行驶消耗对应油量
pos=firstpos; //更新当前位置到便宜加油站
}
else{//范围内没有更便宜的油,当前油价最低
int minpos=pos+1;//记录范围内油价最低的加油站编号
for(int i=pos+1;i<=n;i++){//遍历续航范围内所有站点
if(a[i].d-a[pos].d>dis) break;//超出续航停止
if(a[i].p<a[minpos].p) minpos=i;//更新最低价站点
}
cost+=(c-oil)*a[pos].p;//把油箱加满,累加花费
oil=c;//油箱设为满油状态
double oilto=(a[minpos].d-a[pos].d)/l;//开到最低价站需油量
oil-=oilto;//行驶消耗油量
pos=minpos;//更新当前位置到最低价加油站
}
}
cout<<fixed<<setprecision(2)<<cost<<endl;//输出最小总花费,保留两位小数
return 0;
}
以上就是本题全部内容,细节处理好了就没问题。