最佳飞行路线问题 | 豆包MarsCode AI刷题

123 阅读6分钟

问题描述:小C和他的领导小F计划一次飞行,但由于严格的航空管制,他们的飞机仅能按特定的路线飞行:飞机只能飞往当前机场的相邻机场或相同航空公司管理的机场。为了减少起飞次数,小C需要制定最优的飞行路线。机场由 个数组airports标识,其中:

  • 数组每个元素代表一个独特的机场,元素的值代表不同的航空公司。
  • airports[0]为起点,airports[airports.length-1]为终点。
  • 假设小C当前在机场i,那么i- 1和i+1(如果存在)代表邻近机场,飞机可以直接前往。
  • 如果在机场i,且存在airports[i]== airports[j],则机场i和机场j同属一家航空公司,可直接飞往。

求最小起飞次数

1. 问题背景和需求

题目要求计算从起点机场到终点机场的最短飞行次数,其中飞行路线受到两个限制:

  • 只能飞往相邻的机场(i-1 和 i+1)。
  • 也可以飞往同一航空公司管理的其他机场。

目标是找到从起点机场(airports[0])到终点机场(airports[-1])的最小飞行次数。

2. 图建模

我们将机场视为图中的节点,飞行的路线视为图中的边。每个节点(机场)之间的边表示两机场之间有直接的航班。

  • 如果机场 i 和机场 j 是相邻的(i-1i+1),那么有一条边连接 ij
  • 如果 ij 属于同一家航空公司,即 airports[i] == airports[j],那么它们之间也有一条边,表示可以飞往同一航空公司管理的机场。

3. 算法选择

由于我们需要计算从起点机场到终点机场的最短路径,而每次飞行的“权重”是相同的(即每一次飞行的代价是 1),这个问题符合 无权图最短路径问题。最适合的算法是 广度优先搜索(BFS)

BFS 有几个特点:

  • BFS 保证在找到目标节点时,它是通过最短路径到达的。
  • BFS 会逐层搜索,先探索距离起点最近的节点,确保首先到达终点的路径是最短的。

4. BFS 具体实现分析

BFS 的基本步骤如下:

  1. 初始化

    • 使用一个队列(queue)存储待访问的节点。队列元素是一个二元组 (当前机场索引, 当前飞行次数),表示从起点到当前机场所需要的飞行次数。
    • 使用 visited 集合记录已经访问过的机场,防止重复访问。
  2. 处理每个机场

    • 每次从队列中取出一个机场,检查是否是终点机场。如果是,直接返回飞行次数。
    • 如果不是终点,则有两种方式可以“飞行”:
      1. 飞往相邻机场:如果 i-1i+1 是合法的并且未访问过,加入队列。
      2. 飞往同一航空公司管理的机场:查找所有与当前机场属于同一航空公司的其他机场,并且这些机场没有被访问过,加入队列。
  3. 终止条件

    • 如果队列为空且仍未找到终点,则说明无法到达终点,返回 -1
  4. 优化点

    • 为了防止重复访问同一航空公司管理的机场,我们在每次访问完一个机场后,立即清空该航空公司的机场列表(company_airports[airports[i]] = []),避免后续多次访问。

5. 时间复杂度分析

  • BFS的时间复杂度:BFS 中每个机场最多只会被访问一次,因此总的时间复杂度是 O(n),其中 n 是机场的数量。

  • 查找同一航空公司管理的机场:对于每个机场,我们需要查找所有同一航空公司管理的机场,这个操作可以通过使用 defaultdict(list) 来优化。每个航空公司最多涉及一次完整遍历,但由于我们在每次访问后清空航空公司列表,因此每个机场的所有操作都只会发生一次。

  • 因此,总的时间复杂度仍然是 O(n)

6. 空间复杂度分析

  • 我们需要维护以下几个数据结构:
    • queue:存储待访问的机场,最多存储 n 个机场,因此空间复杂度为 O(n)
    • visited:存储已访问的机场,最坏情况下需要存储 n 个机场,因此空间复杂度为 O(n)
    • company_airports:存储每个航空公司管理的机场列表。最坏情况下,每个机场都属于不同的航空公司,因此也可能需要 O(n) 的空间。

因此,空间复杂度为 O(n)

7. 特殊情况分析

  • 只有一个机场:如果 n == 1,那么起点和终点是同一个机场,最小飞行次数为 0
  • 无法到达终点:如果在搜索过程中,队列为空且未到达终点,返回 -1,表示无法从起点到达终点。

8. 代码实现再优化

在一些极端情况下,比如同一航空公司管理的机场很多,可能会导致同一航空公司重复搜索。为了避免这种情况,可以在每次使用航空公司列表之后,将它清空,确保不重复计算。

完整代码实现:

from collections import deque, defaultdict

def min_takeoffs(airports):
    n = len(airports)
    if n == 1:
        return 0  # 如果只有一个机场,已经是起点和终点了,0次飞行
    
    # 创建航空公司管理的机场映射
    company_airports = defaultdict(list)
    for i, company in enumerate(airports):
        company_airports[company].append(i)
    
    # BFS初始化
    queue = deque([(0, 0)])  # (当前机场索引, 飞行次数)
    visited = set([0])  # 已经访问过的机场
    
    while queue:
        i, takeoffs = queue.popleft()
        
        # 如果到达终点机场,返回当前飞行次数
        if i == n - 1:
            return takeoffs
        
        # 1. 尝试从相邻机场飞行
        if i - 1 >= 0 and (i - 1) not in visited:
            visited.add(i - 1)
            queue.append((i - 1, takeoffs + 1))
        
        if i + 1 < n and (i + 1) not in visited:
            visited.add(i + 1)
            queue.append((i + 1, takeoffs + 1))
        
        # 2. 尝试飞往同一家航空公司管理的机场
        for j in company_airports[airports[i]]:
            if j != i and j not in visited:
                visited.add(j)
                queue.append((j, takeoffs + 1))
        
        # 清空同一航空公司集合,防止重复访问
        company_airports[airports[i]] = []

    # 如果没有找到路径,返回-1(不可能到达终点)
    return -1

# 测试
airports = [1, 2, 1, 3, 1]
print(min_takeoffs(airports))  # 输出应为最小飞行次数

总结

  • 算法设计:使用 BFS 求解最短路径问题,确保每次访问机场时都能保证最少飞行次数。
  • 优化:使用 defaultdictvisited 集合来避免重复访问机场,并通过清空航空公司列表避免重复计算。
  • 时间复杂度O(n),每个机场最多只会被访问一次,且每次访问的时间是常数级的。