咱先see see 题目:
问题描述
小C和他的领导小F计划一次飞行,但由于严格的航空管制,他们的飞机仅能按特定的路线飞行:飞机只能飞往当前机场的相邻机场或相同航空公司管理的机场。为了减少起飞次数,小C需要制定最优的飞行路线。机场由一个数组airports标识,其中:
- 数组每个元素代表一个独特的机场,元素的值代表不同的航空公司。
airports[0]为起点,airports[airports.length - 1]为终点。- 假设小C当前在机场
i,那么i - 1和i + 1(如果存在)代表邻近机场,飞机可以直接前往。 - 如果在机场
i,且存在airports[i] == airports[j],则机场i和机场j同属一家航空公司,可直接飞往。 求最小起飞次数。
一开始看到题目,嘿这不动态规划嘛,秒了。。。
意料之中的的WA,虽然题面给的测试样例过了。但卡在了如下样例: [7 16 3 10 17 21 7 16 12 10 5 17 13 25 17 18 22 21 6 11 20 13 20]
(画个Mermaid,效果很感人:))
stateDiagram-v2
1 --> 2
2 --> 1
2 --> 3
3 --> 2
3 --> 4
4 --> 3
4 --> 5
5 --> 4
5 --> 6
6 --> 5
6 --> 7
7 --> 6
7 --> 8
8 --> 7
8 --> 9
9 --> 8
9 --> 10
10 --> 9
10 --> 11
11 --> 10
11 --> 12
12 --> 11
12 --> 13
13 --> 12
13 --> 14
14 --> 13
14 --> 15
15 --> 14
15 --> 16
16 --> 15
16 --> 17
17 --> 16
17 --> 18
18 --> 17
18 --> 19
19 --> 18
19 --> 20
20 --> 19
20 --> 21
21 --> 20
21 --> 22
22 --> 21
22 --> 23
23 --> 22
1 --> 7
7 --> 1
2 --> 8
8 --> 2
4 --> 10
10 --> 4
5 --> 12
12 --> 5
5 --> 15
15 --> 5
12 --> 15
15 --> 12
13 --> 22
22 --> 13
21 --> 23
23 --> 21
6 --> 18
18 --> 6
图上看的很明显(大概),不能粗暴的dp,因为飞机可以往回飞,也可能可以dp,只是我太菜了:(
最终用粗暴的BFS
正文开始:)
解题思路
这个问题可以看作是一个图的最短路径问题。每个机场可以看作图中的一个节点,节点之间的边表示机场之间的飞行关系。具体来说:
-
节点:数组
airports中的每个元素代表一个节点。 -
边:
- 相邻机场之间存在边(即
i与i-1和i+1之间)。 - 同一家航空公司的机场之间存在边(即
airports[i] == airports[j])。
- 相邻机场之间存在边(即
为了找到从起点到终点的最短路径,我们可以使用广度优先搜索(BFS)算法。BFS算法非常适合解决这类问题,因为它可以保证在找到目标节点时,路径是最短的。
数据结构选择
- 队列:用于BFS算法,存储当前节点及其对应的起飞次数。
- 集合:用于记录已经访问过的节点,避免重复访问。
- 哈希表:用于存储同一家航空公司的机场索引,便于快速查找。
算法步骤
-
初始化:
- 创建一个队列
queue,用于存储当前节点及其对应的起飞次数。 - 创建一个集合
visited,用于记录已经访问过的节点。 - 创建一个哈希表
map,用于存储同一家航空公司的机场索引。
- 创建一个队列
-
构建图:
- 遍历
airports数组,将同一家航空公司的机场索引存储到哈希表中。
- 遍历
-
BFS搜索:
- 将起点(
airports[0])及其起飞次数(0)入队。 - 当队列不为空时,取出队首元素,检查其是否为终点。
- 如果是终点,更新最小起飞次数。
- 如果不是终点,将其相邻机场和同一家航空公司的机场加入队列,并标记为已访问。
- 将起点(
-
返回结果:
- 如果找到路径,返回最小起飞次数;否则返回-1。
代码实现
public class Main {
public static int solution(int[] airports) {
// 创建队列和集合
int res = Integer.MAX_VALUE;
Queue<int[]> queue = new LinkedList<>();
Set<Integer> visited = new HashSet<>();
Map<Integer, ArrayList<Integer>> map = new HashMap<>();
for (int i = 0; i < airports.length; i++) {
if (map.containsKey(airports[i])) {
map.get(airports[i]).add(i);
} else {
ArrayList<Integer> t = new ArrayList<>();
t.add(i);
map.put(airports[i], t);
}
}
// 将起点机场和起飞次数入队
queue.offer(new int[]{0, 0});
while (!queue.isEmpty()) {
int[] current = queue.poll();
int index = current[0];
int steps = current[1];
// 如果到达终点机场,返回起飞次数xd
if (index == airports.length - 1) {
// System.out.println(steps);
res = Math.min(res, steps);
}
// 标记当前机场为已访问
if (visited.contains(index)) {
continue;
} else {
visited.add(index);
}
if (index >= airports.length) break;
// 将相邻机场和同一家航空公司的机场加入队列
if (index - 1 >= 0) queue.offer(new int[]{index - 1, steps + 1});
if (map.containsKey(airports[index])) {
for (int i : map.get(airports[index])) {
if (i == index) continue;
queue.offer(new int[]{i, steps + 1});
}
}
if (index + 1 <= airports.length - 1) queue.offer(new int[]{index + 1, steps + 1});
}
// 如果没有找到路径,返回-1
return res;
}
}
总的来说还是一道比较简单题,虽然是中等题的标签
to be continue :))