贪心算法-汽车加油
这道题目描述了一个汽车旅行场景,需要设计一个有效的算法来决定在哪几个加油站停车加油,以便最小化加油次数。题目给出了汽车加满油后的行驶距离n公里,以及沿途若干个加油站的位置。我们需要找出一个方案,使得汽车能够完成整个旅程,同时加油次数最少。
为了更好地理解并解决问题,我们可以将其转化为数学模型和算法设计问题。首先,明确以下几个关键点:
- 汽车的行驶能力:汽车加满油后可以行驶n公里。
- 加油站分布:沿路有k个加油站,每个加油站的具体位置已知。
- 目标:找到一条路线,使得汽车能够完成全程,同时加油次数最少。
解决方案
我们可以使用贪心算法来解决这个问题。贪心策略的基本思想是从局部最优解入手,一步步构造全局最优解。在这里,我们的局部最优决策就是每次选择离当前位置最远的那个加油站作为下一个加油地点。
步骤如下:
-
初始化变量:
- 当前位置设为起点。
- 加油次数计数器置零。
-
迭代过程:
- 遍历所有的加油站,寻找离当前位置最远但又不会超过汽车行驶范围的加油站。
- 如果找到了这样的加油站,就在那里加油,并更新当前位置。
- 同时,加油次数计数器加一。
-
结束条件:
- 如果没有找到合适的加油站,说明当前油量不足以达到任何一个加油站,返回“No Solution”。
- 如果已经到达终点,停止搜索。
实现细节
- 数据结构:可以用一个列表或数组来保存加油站的位置。
- 算法流程:按照上述步骤编写程序逻辑,需要注意的是,在每次选择加油站之后,都要检查是否还能到达下一个加油站,否则就需要再次加油。
注意事项
- 确保加油站的位置是按顺序排列的,这样可以简化查找过程。
- 在实际编码过程中,要注意边界条件的处理,避免出现越界错误。
通过这种方法,我们可以有效地解决这个问题,找到最少加油次数的方案。如果在某一步发现无法前进,那么就表明没有可行的解决方案,此时应返回“No Solution”。
假设我们有一个int[] gasStations
数组,它包含了从起点到终点所有加油站的位置(公里数),并且汽车加满油后能行驶的最大距离为int maxDistance
。我们将从起点开始尝试到达终点,途中尽可能少地加油。
import java.util.Arrays;
public class MinimumRefuelStops {
public static void main(String[] args) {
int maxDistance = 10; // 汽车加满油后能行驶的最大距离
int[] gasStations = {2, 5, 7, 10}; // 加油站位置,单位为公里
int destination = 10; // 目的地的位置
System.out.println(minimumRefuelStops(maxDistance, gasStations, destination));
}
/**
* 计算最少加油次数。
*
* @param maxDistance 汽车加满油后能行驶的最大距离
* @param gasStations 加油站位置数组
* @param destination 目的地的位置
* @return 最少加油次数,若无法到达目的地则返回"No Solution"
*/
public static String minimumRefuelStops(int maxDistance, int[] gasStations, int destination) {
int currentPosition = 0; // 当前位置
int refuelCount = 0; // 加油次数
int nextStationIndex = 0; // 下一个加油站的索引
while (currentPosition < destination) {
// 找到最远可以到达的加油站
while (nextStationIndex < gasStations.length && gasStations[nextStationIndex] <= currentPosition + maxDistance) {
nextStationIndex++;
}
// 如果当前位置已经超过了最后一个加油站,但仍未到达目的地
if (nextStationIndex == gasStations.length && currentPosition + maxDistance < destination) {
return "No Solution";
}
// 如果当前位置已经可以到达或超过目的地
if (currentPosition + maxDistance >= destination) {
break;
}
// 如果有可用的加油站,选择最远的一个加油
if (nextStationIndex > 0) {
currentPosition = gasStations[nextStationIndex - 1];
refuelCount++;
} else {
// 没有可以到达的加油站
return "No Solution";
}
}
return String.valueOf(refuelCount);
}
}
这段代码中,我们定义了一个minimumRefuelStops
方法来计算最少加油次数。该方法首先初始化当前位置和加油次数,然后在一个循环中不断寻找最远可以到达的下一个加油站,直到汽车能够到达目的地或者确定无法到达目的地为止。
注意,这个例子假设了gasStations
数组中的元素已经按照从小到大的顺序排序,这是合理的,因为加油站的位置应该是沿着路径递增的。如果实际情况不是这样,您可能需要先对gasStations
数组进行排序。
在每次决定是否加油时,贪心算法会选择在当前油量不足以到达下一个加油站或目的地时才进行加油。这种选择保证了每次加油都是必要的,避免了不必要的加油操作,从而减少了总的加油次数。
package tanxin;
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
public class CarsStop {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int n = scanner.nextInt(); // 输入加满油可行驶的公里数
int k = scanner.nextInt(); // 输入加油站数量
int[] distances = new int[k + 1]; // 定义数组存储相邻加油站之间的距离,最后一个元素是最后一个加油站到目的地的距离
for (int i = 0; i < k; i++) {
distances[i] = scanner.nextInt(); // 读取相邻加油站之间的距离
}
distances[k] = scanner.nextInt(); // 最后一个加油站到目的地的距离
int m = 0; // 初始化加油次数为0
int t = n; // 汽车开始可行驶的公里数
List<Integer> stations = new ArrayList<>(); // 存储加油的加油站编号
for (int i = 0; i <= k; i++) { // 包括最后一个加油站到目的地的距离
if (distances[i] > n) { // 如果某段距离大于汽车的最大行驶距离,输出无解并退出程序
System.out.println("No Solution");
return;
}
if (t < distances[i]) { // 如果当前剩余油量不足以到达下一个地点,则需要加油
t = n; // 汽车加一次油,汽车能行驶的距离变为n
m++; // 加油次数+1
stations.add(i - 1); // 记录加油的加油站编号,注意这里是i-1因为是在到达i站之前加油
}
t -= distances[i]; // 减去已行驶的距离
}
System.out.println("加油次数为:" + m); // 输出加油次数
if (!stations.isEmpty()) {
System.out.print("加油地点:");
for (Integer station : stations) {
System.out.print("第" + (station + 1) + "个加油站, ");
}
System.out.println(); // 换行
}
}
}
-
输入读取:
- 首先,程序通过
Scanner
类读取用户输入的数据。包括汽车加满油后可以行驶的最大距离n
和加油站的数量k
。 - 然后,程序读取每个加油站之间的距离(存入
distances
数组),以及最后一个加油站到目的地的距离。
- 首先,程序通过
-
初始化变量:
m
变量用于记录加油次数,初始值为0。t
变量代表汽车当前还剩多少公里可以行驶,初始值为n
,即汽车加满油后的最大行驶距离。stations
列表用来存储每次加油时所在的加油站编号。
-
核心逻辑:
- 使用一个循环遍历所有加油站的距离数据,包括从最后一个加油站到目的地的距离。
- 对于每一个距离
distances[i]
,首先检查该距离是否超过了汽车的最大行驶距离n
,如果是,则输出“无解”并结束程序。 - 接着,判断当前剩余油量
t
是否足够行驶至下一个加油站或目的地。如果不够,则需要在当前加油站加油,并更新相关变量:- 将
t
重置为n
,表示汽车加满油后能行驶的最大距离。 - 增加加油次数
m
。 - 记录加油的加油站编号
i - 1
到stations
列表中(注意这里是因为加油是在到达下一个加油站之前完成的)。
- 将
- 更新
t
,减去已经行驶过的距离distances[i]
。
-
输出结果:
- 循环结束后,输出总的加油次数
m
。 - 如果有加油记录,还会输出具体的加油地点。
- 循环结束后,输出总的加油次数
示例运行
假设输入如下:
7 7
1 2 3 4 5 1 6 1
这表示汽车加满油后可以行驶的最大距离为7公里,总共有7个加油站,各加油站间的距离分别为1, 2, 3, 4, 5, 1公里,最后一个加油站到目的地的距离为6公里。