电路板上布线的“最短路径”问题

     对于这个“布线问题”,我们可以将其看作在网格中寻找最短路径的问题。要求路径只能沿着横向或纵向移动,还可以通过“日”字形的弯曲绕过障碍物。这里,我们采用 广度优先搜索(BFS) 来解决这个问题。

BFS 的基本原理

BFS 的核心思想是逐层扩展节点,以广度优先的方式从起点逐步访问相邻节点,直到找到目标节点或遍历完整个图。BFS 使用 队列 来保持访问顺序,每次从队列中取出节点,将其相邻节点加入队列中,直到队列为空。

BFS 的过程可简化为以下步骤:

  1. 将起点加入队列,标记为已访问。
  2. 从队列中取出一个节点,将所有未访问过的相邻节点加入队列并标记为已访问。
  3. 重复步骤 2,直到队列为空或找到目标节点。

BFS 的特点:

  • BFS 是一种“层次遍历”,即先访问距离起点为 1 的节点,再访问距离为 2 的节点,依此类推。
  • BFS 适合寻找 无权图(边没有权重)中的最短路径。

问题分析

输入数据
  1. 给定一个 m x n 的网格,每个单元格要么可以通过(标记为 0),要么是障碍物(标记为 1)。
  2. 起点 a 和终点 b,我们需要找到从 ab 的最短路径长度。
输出

返回路径长度(如果路径存在),或 -1 表示无法到达。

思路

  1. 广度优先搜索 (BFS)

    • BFS 是一种层层扩展节点的搜索算法,适合用于寻找无权图的最短路径。
    • 我们从起点 a 开始,把相邻的节点逐步加入到队列中,并逐层搜索,直到找到终点 b
  2. 方向控制

    • 定义四个方向:上、下、左、右。
    • 在每个位置,我们检查四个方向的相邻格子,判断它们是否可以通行。
    • 如果前进方向发生变化,我们就更新路径长度,并继续扩展新的位置。
  3. 避免回到已访问位置

    • 使用一个访问集合 visited,记录每个位置的访问状态,以避免重复访问相同的格子。
  4. 障碍物处理

    • 在 BFS 中,如果遇到障碍物格子(值为 1),我们跳过这个格子。

代码实现

from collections import deque

# 定义四个方向:右,下,左,上
DIRECTIONS = [(0, 1), (1, 0), (0, -1), (-1, 0)]

def is_within_bounds(x, y, m, n):
    """检查坐标是否在网格边界内"""
    return 0 <= x < m and 0 <= y < n

def shortest_path(grid, start, end):
    m, n = len(grid), len(grid[0])
    
    # 创建一个队列,用于广度优先搜索,初始化时包含起点坐标、方向和路径长度
    queue = deque([(start[0], start[1], -1, 0)])  # 格式:x, y, direction, path_length
    visited = set()
    visited.add((start[0], start[1], -1))  # 起点标记为已访问

    while queue:
        x, y, direction, path_length = queue.popleft()

        # 检查是否到达终点
        if (x, y) == end:
            return path_length

        # 遍历四个方向
        for d, (dx, dy) in enumerate(DIRECTIONS):
            nx, ny = x + dx, y + dy
            
            # 检查新坐标是否在边界内,是否可以通行,以及是否已访问过
            if is_within_bounds(nx, ny, m, n) and grid[nx][ny] == 0:
                # 如果方向改变(实现“日”字形路径),更新路径长度
                if d != direction:
                    if (nx, ny, d) not in visited:
                        visited.add((nx, ny, d))
                        queue.append((nx, ny, d, path_length + 1))
                else:
                    # 如果方向没有改变,路径长度保持不变
                    if (nx, ny, direction) not in visited:
                        visited.add((nx, ny, direction))
                        queue.append((nx, ny, direction, path_length + 1))

    # 如果找不到路径,返回 -1
    return -1

# 示例网格,0 表示可通行,1 表示障碍
grid = [
    [0, 0, 0, 1, 0],
    [1, 0, 1, 1, 0],
    [0, 0, 0, 0, 0],
    [1, 1, 0, 1, 1],
    [0, 0, 0, 0, 0]
]

# 起点和终点
start = (0, 0)
end = (4, 4)

# 调用函数计算最短路径
result = shortest_path(grid, start, end)
print("最短路径长度:", result)

代码解析

  • DIRECTIONS:定义了四个方向:右、下、左、上。
  • is_within_bounds:一个辅助函数,检查坐标是否在网格边界内。
  • shortest_path
    • 初始化 queue,将起点加入队列。队列存储的是 (x, y, direction, path_length) 格式的信息,其中 direction 用来记录上一次移动的方向。
    • 使用 BFS 遍历网格,层层扩展节点。
    • 每次扩展时,检查新方向 d 是否和之前的 direction 相同,以此控制“日”字形转向。
    • 如果找到终点,则返回路径长度。
    • 如果找不到路径,则返回 -1。

示例输出

假设网格如下所示:

0 0 0 1 0
1 0 1 1 0
0 0 0 0 0
1 1 0 1 1
0 0 0 0 0

起点为 (0, 0),终点为 (4, 4),运行代码的输出为:

最短路径长度: 8

复杂度分析

  • 时间复杂度O(m * n),每个格子最多被访问一次。
  • 空间复杂度O(m * n),用于存储队列和访问集合 visited

总结

此代码利用 BFS 实现了最短路径搜索,并控制了“日”字形转向。通过方向判断,确保路径在遇到障碍物时能够绕行,且避免重复访问已探索的格子。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值