问题描述
一个旅行家想驾驶汽车以最少的费用从一个城市到另一个城市(假设出发时油箱是空的)。给定两个城市之间的距离D1、汽车油箱的容量C(以升为单位)、每升汽油能行驶的距离D2、出发点每升汽油价格P和沿途油站数N(N可以为零),油站i离出发点的距离Di、每升汽油价格Pi(i=1,2,……N)。计算结果四舍五入至小数点后两位。如果无法到达目的地,则输出“No Solution”。
输入格式
第一行为4个实数D1、C、D2、P与一个非负整数N;
接下来N行,每行两个实数Di、Pi。
输出格式
如果可以到达目的地,输出一个实数(四舍五入至小数点后两位),表示最小费用;否则输出“No Solution”(不含引号)。
样例输入
275.6 11.9 27.4 2.8 2
102.0 2.9
220.0 2.2
样例输出
26.95
这题的变量比较多,所以在此之前我们得捋一捋变量之间的关系。根据题目所给的变量我们还可以得出汽车满油状态下能行驶的里程。
我把已知变量列出如下。
double D1=sc.nextDouble();//两个城市之间的距离
double C=sc.nextDouble();//汽车油箱的容量
double D2=sc.nextDouble();//每升油能行驶的距离
double P=sc.nextDouble();//出发点的油价
int N=sc.nextInt();//沿途油站数
double[] Pi=new double[N+2];//每个站点的油价
double[] D=new double[N+2];//离出发点的距离
double MaxDistance=D2*C;
double fee=0;//总费用
double remain=0;//汽车剩余的油量
算法分析
我们需要求的结果是最少的费用。因此我们每个加油站的费用是我们要考虑的重要因素之一。
首先我们排除无解的情况,只要存在两个节点的距离大于汽车能行驶的最大距离则无解。(PS:循环是从0到N,因为在初始化数据D与Pi时我将起点与终点也加入了进去,完整代码贴在最后。)
for(int i=0;i<=N;i++)
if(D[i+1]-D[i]>MaxDistance) {
System.out.println("No Solution");
return ;
}
我们总是希望在加油的时候,能在汽车可行驶的最大距离MaxDistance之中,找到油价更低的加油站进行加油。
基于此我们考虑可能的两种情况。每次到达加油的站点 i时做一次判断。
1.从当前站点的下一个站点i+1到汽车能行驶的最大距离MaxDistance之间存在油价更低的位置j
那么我们就从当前站点i加油,刚好能到达j。
2.当前站点的下一个站点j到j+MaxDistance之间不存在油价更低的位置
这种情况下我们就把油加满!因为这个地方是目前而言油价最低的点,那我们就需要贪心一点,有多少加多少。直到能跑到最远的位置。
那么现在问题来了,我们要如何定位——从当前位置i出发,到下一站j?
遍历i站点的下一个站点i+1到终点N+1的每一个站点,分两步走:
1.如果从当前位置到下一个站点者之间的距离大于MaxDistance,退出循环,说明汽车能到达的位置是j-1,即必须要到j-1这一站加油了!
2.如果出现了比i站点油价便宜的地儿,因为旅行家是贪心的,所以我们得锁定这个j,立马跑到这个站点去加油。
int j=i;
for(j=i+1;j<=N+1;j++)
{
if (D[j]-D[i]>MaxDistance) {
j-=1;//最大行驶距离无法到达j,因此最大到达j-1站点
break;
}
if(Pi[j]<=Pi[i])
break;//找到了下一个更便宜的加油站
}
到了现在我们找到了每加一次油后,接着要前往的下一站。按照一开始讨论的两种情况,对
if(Pi[j]<=Pi[i]) //找到了下一个更便宜的加油站
{
fee+=((D[j]-D[i])/D2-remain)*Pi[i];//更新费用
remain=0;//更新到达下一个加油站后的剩余油量
}
else//没有找到加满油
{
fee+=(C-remain)*Pi[i];
remain=C-(D[j]-D[i])/D2;
}
整个过程就是这样,完整代码如下
import java.util.*;
public class 旅行家的预算 {
public static void main(String[] args)
{
Scanner sc=new Scanner(System.in);
double D1=sc.nextDouble();//两个城市之间的距离
double C=sc.nextDouble();//汽车油箱的容量
double D2=sc.nextDouble();//每升油能行驶的距离
double P=sc.nextDouble();//出发点的油价
int N=sc.nextInt();//沿途油站数
double[] Pi=new double[N+2];//每个站点的油价
double[] D=new double[N+2];//离出发点的距离
double MaxDistance=D2*C;
//初始化距离和油价数组,将起点与终点也加入其中
D[0]=0;Pi[0]=P;
D[N+1]=D1;Pi[N+1]=0;
for(int i=1;i<=N;i++) {
D[i]=sc.nextDouble();
Pi[i]=sc.nextDouble();
}
double fee=0;
double remain=0;
//无解的情况
for(int i=0;i<=N;i++)
if(D[i+1]-D[i]>MaxDistance) {
System.out.println("No Solution");
return ;
}
//有解,开始遍历每一个站点
int i=0;
while(i<=N)
{
int j;
for(j=i+1;j<=N+1;j++)
{
if (D[j]-D[i]>MaxDistance)
{
j-=1;//最大行驶距离无法到达j,因此最大到达j-1站点
break;
}
if(Pi[j]<=Pi[i])
break;//找到下一个更便宜的加油站
}
if(Pi[j]<=Pi[i]) //找到了下一个更便宜的加油站
{
fee+=((D[j]-D[i])/D2-remain)*Pi[i];//更新费用
remain=0;//更新到达下一个加油站后的剩余油量
}
else//没有找到加满油
{
fee+=(C-remain)*Pi[i];
remain=C-(D[j]-D[i])/D2;
}
i=j;//前往下一个加油站,滴滴滴!
}
System.out.println(String.format("%.2f", fee));
}
}
有一点要注意的是倒数第二行,每次循环完毕后,要把下一站的值赋给i,表示我们即将前往的下一站。