阿 D 的最佳飞行路线探索题解 | 豆包MarsCode AI 刷题

47 阅读6分钟

阿 D 的最佳飞行路线探索

问题描述

小 C 和他的领导小 F 计划一次飞行,但由于严格的航空管制,他们的飞机仅能按特定的路线飞行:

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

目标:求最小起飞次数。

测试样例

  • 样例 1: 输入:airports = [10, 12, 13, 12, 14] 输出:3

  • 样例 2: 输入:airports = [10, 11, 12, 13, 14] 输出:4


解题思路

1. 问题分析

这是一个典型的最短路径问题,需要在给定的约束条件下,从起点(airports[0])到达终点(airports[n - 1])用最少的步骤。

移动规则

  • 可以移动到相邻的机场(i - 1i + 1)。
  • 可以直接飞往同一航空公司管理的任何机场(即 airports[i] == airports[j])。

2. 解法概述

使用 广度优先搜索(BFS) 算法:

  • 节点:机场的索引位置。
  • 相邻机场之间的边(ii - 1i + 1)。
  • 同一航空公司机场之间的边(所有 airports[i] == airports[j])。

3. 算法步骤

  1. 构建航空公司映射:建立航空公司与其管理的机场列表的映射,便于快速找到同一航空公司的其他机场。

  2. 初始化 BFS

  • 使用队列 queue 存储待访问的机场及其对应的起飞次数。
  • 使用数组 visited 标记机场是否已访问过,避免重复访问。
  1. 开始 BFS 遍历
  • 从起点开始,将其加入队列。
  • 依次弹出队列中的机场,探索可以到达的下一个机场:
    • 相邻机场:如果未访问过,加入队列。
    • 同一航空公司的机场:如果未访问过,加入队列。
  • 记录已访问的航空公司,避免重复处理同一航空公司的机场列表。
  1. 终止条件
  • 当到达终点机场时,返回当前的起飞次数。
  • 如果队列为空且未到达终点,返回 -1(根据题目描述,不会发生)。

4. 示例分析

样例 1

airports = [10, 12, 13, 12, 14]

  • 起点:索引 0,航空公司 10。
  • 终点:索引 4,航空公司 14。

步骤

image.png

  1. 第 0 步

    • 当前机场:0,起飞次数:0。
    • 相邻机场:1(未访问),加入队列。
    • 同一航空公司机场:无(航空公司 10 只有机场 0)。
  2. 第 1 步

    • 当前机场:1,起飞次数:1。
    • 相邻机场:2(未访问),加入队列。
    • 同一航空公司机场:3(airports[1] == airports[3] == 12),加入队列。
  3. 第 2 步

    • 当前机场:2,起飞次数:2。
    • 相邻机场:3(已访问),4(未访问),加入队列。
    • 同一航空公司机场:无。
  4. 第 3 步

    • 当前机场:3,起飞次数:2。
    • 已访问,不重复处理。
  5. 第 4 步

    • 当前机场:4,起飞次数:3。
    • 到达终点,返回起飞次数 3。

代码详解

from collections import deque, defaultdict

def solution(airports):
    n = len(airports)
    if n == 1:
        return 0  # 起点和终点相同,无需起飞

    # 构建航空公司所属机场的映射
    airline_map = defaultdict(list)
    for i, company in enumerate(airports):
        airline_map[company].append(i)
    
    # BFS 队列,存储 (当前机场索引, 当前起飞次数)
    queue = deque([(0, 0)])
    visited = [False] * n  # 记录机场是否访问过
    visited[0] = True  # 起点已访问
    visited_airlines = set()  # 记录已访问的航空公司,避免重复处理

    while queue:
        current, flights = queue.popleft()
        
        # 如果到达终点,返回起飞次数
        if current == n - 1:
            return flights
        
        # 考虑相邻机场
        for neighbor in [current - 1, current + 1]:
            if 0 <= neighbor < n and not visited[neighbor]:
                visited[neighbor] = True
                queue.append((neighbor, flights + 1))
        
        # 考虑同一航空公司的机场
        company = airports[current]
        if company not in visited_airlines:
            visited_airlines.add(company)
            for neighbor in airline_map[company]:
                if not visited[neighbor]:
                    visited[neighbor] = True
                    queue.append((neighbor, flights + 1))
    
    return -1  # 如果无法到达终点,返回 -1(实际不会发生)

代码解释

  • 导入模块

    • deque:高效的队列,实现 BFS。
    • defaultdict:默认字典,用于存储航空公司映射。
  • 函数定义def solution(airports):

  • 初始化

    • n:机场数量。
    • 特例处理:如果只有一个机场,返回 0。
  • 构建航空公司映射

    python
    复制代码
    airline_map = defaultdict(list)
    for i, company in enumerate(airports):
        airline_map[company].append(i)
    
    • airline_map:键为航空公司,值为该航空公司管理的机场索引列表。
  • 初始化 BFS 队列和访问数组

    python
    复制代码
    queue = deque([(0, 0)])
    visited = [False] * n
    visited[0] = True
    visited_airlines = set()
    
    • queue:存储待访问的机场及其起飞次数。
    • visited:标记机场是否已访问,防止重复访问。
    • visited_airlines:记录已处理过的航空公司,避免重复遍历同一航空公司的机场列表。
  • BFS 主循环

    python
    复制代码
    while queue:
        current, flights = queue.popleft()
    
    • 弹出队首元素,获取当前机场和起飞次数。
  • 检查是否到达终点

    python
    复制代码
    if current == n - 1:
        return flights
    
  • 探索相邻机场

    python
    复制代码
    for neighbor in [current - 1, current + 1]:
        if 0 <= neighbor < n and not visited[neighbor]:
            visited[neighbor] = True
            queue.append((neighbor, flights + 1))
    
    • 检查左邻和右邻机场是否存在且未访问,加入队列。
  • 探索同一航空公司的机场

    python
    复制代码
    company = airports[current]
    if company not in visited_airlines:
        visited_airlines.add(company)
        for neighbor in airline_map[company]:
            if not visited[neighbor]:
                visited[neighbor] = True
                queue.append((neighbor, flights + 1))
    
    • 如果当前航空公司未处理过,遍历该航空公司管理的所有机场,若未访问过,加入队列。
  • 返回结果

    • 如果遍历结束仍未到达终点,返回 -1(根据题目描述,不会发生)。

知识总结

1. 广度优先搜索(BFS)

  • 定义:一种遍历或搜索数据结构(如树或图)的算法。

  • 特点

    • 使用队列实现,先入先出。
    • 按照层次逐层遍历,先访问离起点最近的节点。

2. 图的表示与遍历

  • 邻接表:用列表或字典表示图中节点的相邻关系。
  • 访问标记:使用数组或集合记录已访问的节点,避免重复遍历。

3. defaultdict 的使用

  • 作用:当字典的键不存在时,自动为其创建默认值,避免 KeyError
  • 应用:在构建航空公司映射时,便于添加机场索引列表。

4. 算法优化

  • 避免重复处理

    • 对已访问的机场和航空公司进行标记,减少不必要的计算。
  • 时间复杂度

    • 因为每个机场和航空公司最多访问一次,时间复杂度为 O(n)

学习建议

对于初学者

  1. 理解 BFS 的核心思想:掌握 BFS 的实现方式和适用场景,对于解决最短路径问题非常有用。

  2. 熟练使用数据结构

    • 队列:了解如何使用 deque 实现队列操作。
    • 字典和集合:掌握 defaultdict 和集合的用法,提高代码的简洁性和效率。
  3. 练习图的遍历和搜索

    • 多做一些图相关的算法题,熟悉不同的图表示方法和遍历技巧。