1.题目
题目描述
一个旅行家想驾驶汽车以最少的费用从一个城市到另一个城市(假设出发时油箱是空的)。给定两个城市之间的距离 D 1 D1 D1、汽车油箱的容量 C C C(以升为单位)、每升汽油能行驶的距离 D 2 D2 D2、出发点每升汽油价格 P P P 和沿途加油站数 N N N( N N N 可以为零),油站 i i i 离出发点的距离 D i Di Di、每升汽油价格 P i Pi Pi ( i = 1 , 2 , . . . , N i=1,2,...,N i=1,2,...,N)。计算结果四舍五入至小数点后两位。如果无法到达目的地,则输出“No Solution”。
输入格式
第一行,
D
1
D1
D1,
C
C
C,
D
2
D2
D2,
P
P
P,
N
N
N。
接下来有
N
N
N 行。
第
i
+
1
i+1
i+1 行,两个数字,油站
i
i
i 离出发点的距离
D
i
Di
Di 和每升汽油价格
P
i
Pi
Pi。
输出格式
所需最小费用,计算结果四舍五入至小数点后两位。如果无法到达目的地,则输出“No Solution”。
输入输出样例
输入 #1
275.6 11.9 27.4 2.8 2
102.0 2.9
220.0 2.2
输出 #1
26.95
输入 #2
87.75 13.03 5.75 7.29 3
22.10 7.38
24.21 6.81
82.08 6.96
输出 #2
105.95
输入 #3
475.6 11.9 27.4 14.98 6
102.0 9.99
220.0 13.29
256.3 14.79
275.0 10.29
277.6 11.29
381.8 10.09
输出 #3
192.15
说明/提示
N ≤ 6 N \le 6 N≤6,其余数字 ≤ 500 \le 500 ≤500。
2.解题思路
主要思路:贪心+模拟。
- 1.先不管最终结果是否是最小的,应先判断汽车从出发点开始行驶,能否走到每一个加油站,即是否存在相邻两个加油站之间的距离、出发点到第一个加油站之间的距离以及最后一个加油站到目的地的距离超过 C × D 2 C \times D2 C×D2,若存在,则不论旅行者如何加油都不能到达目的地,则输出“No Solution”。
- 2.从出发点出发,寻找出发点之后最大行驶范围内的油价最低的加油站(出发点的油价也纳入比较):若出发点的油价最低,则加满油,寻找最大行驶范围内油价最小的加油站,前往该加油站;若出发点的油价并非最低,则加油使能够到达该加油站,前往该加油站;若不能到达,则加满油,寻找最大行驶范围内油价最低的加油站,前往该加油站。
- 3.若到达目的地,则输出油价花销,否则将到达的加油站设为出发点,重复第2步。
3.代码
import java.util.Scanner;
/**
* @author xiaoyaosheny
* @discription 洛谷P1016 [NOIP1999 提高组] 旅行家的预算
* @date 2021/7/27
*/
public class Main {
/**
* 程序入口
* @param args 输入参数
*/
public static void main(String[] args) {
// 获取输入
Scanner scanner = new Scanner(System.in);
double D1 = scanner.nextDouble(); // 两城市之间的距离
double C = scanner.nextDouble(); // 油箱容量(单位:升)
double D2 = scanner.nextDouble(); // 每升油最多行驶距离
double P = scanner.nextDouble(); // 出发点油价
int N = scanner.nextInt(); // 两城市间的加油站数目
if (N == 0) { // N为零
if (D1 > C * D2) System.out.println("No Solution");
else System.out.printf("%.2f%n", D1 / D2 * P);
}
else { // N不为零
double[] d = new double[N + 1]; // 加油站到出发点的距离
double[] p = new double[N + 1]; // 加油站油价
d[0] = 0; // 出发点
p[0] = P; // 出发点油价
for (int i = 1; i <= N; i++) {
d[i] = scanner.nextDouble();
p[i] = scanner.nextDouble();
}
// 判断能否到达目的地
for (int i = 1; i <= N; i++) {
if (d[i] - d[i - 1] > C * D2) {
System.out.println("No Solution");
return;
}
}
if (D1 - d[N] > C * D2) {
System.out.println("No Solution");
return;
}
// 贪心+模拟
int index = 0; // 汽车当前位置下标
double distance = 0; // 汽车当前位置到出发点的距离
double pMin = 1000; // 汽车当前位置之后所有加油站中的最小油价
double result = 0; // 最终结果
double l = 0; // 油箱剩余油量
double price = P; // 汽车当前位置的油价
while(D1 - distance > 0) { // 当汽车未到达目的地
// 寻找汽车当前位置之后最大行驶范围内的所有加油站中最小油价的加油站
for (int i = index + 1; i <= N && d[i] - distance <= C * D2; i++) {
if (p[i] < pMin) {
pMin = p[i];
index = i;
}
}
// 若出发点油价并非最小油价,加油至刚好能够到达该加油站
if (pMin <= price) {
result += ((d[index] - distance) / D2 - l) * price; // 更新最终结果
l = (d[index] - distance) / D2; // 更新油箱剩余油量
}
// 若出发点油价是最小油价,而汽车当前位置无法直接到达目的地,则加满油
else if (D1 - distance > C * D2) {
result += (C - l) * price; // 更新最终结果
l = C; // 更新油箱剩余油量
}
// 若出发点油价是最小油价,而汽车当前位置能够直接到达目的地,则加油至刚好能够到达目的地
else {
result += ((D1 - distance) / D2 - l) * price; // 更新最终结果
break; // 到达目的地结束模拟
}
l = l - (d[index] - distance) / D2; // 前往加油站,更新油箱剩余油量
distance = d[index]; // 更新出发点位置,即汽车当前位置
price = pMin; // 更新汽车当前位置时的油价
pMin = 1000; // 还原油价最小值,便于比较
}
// 输出结果
System.out.printf("%.2f", result);
}
}
}
4.参考
https://www.luogu.com.cn/blog/li-dongxiao-dx-duxiu/li-dongxiao-dx-duxiu