对于这个“布线问题”,我们可以将其看作在网格中寻找最短路径的问题。要求路径只能沿着横向或纵向移动,还可以通过“日”字形的弯曲绕过障碍物。这里,我们采用 广度优先搜索(BFS) 来解决这个问题。
BFS 的基本原理
BFS 的核心思想是逐层扩展节点,以广度优先的方式从起点逐步访问相邻节点,直到找到目标节点或遍历完整个图。BFS 使用 队列 来保持访问顺序,每次从队列中取出节点,将其相邻节点加入队列中,直到队列为空。
BFS 的过程可简化为以下步骤:
- 将起点加入队列,标记为已访问。
- 从队列中取出一个节点,将所有未访问过的相邻节点加入队列并标记为已访问。
- 重复步骤 2,直到队列为空或找到目标节点。
BFS 的特点:
- BFS 是一种“层次遍历”,即先访问距离起点为 1 的节点,再访问距离为 2 的节点,依此类推。
- BFS 适合寻找 无权图(边没有权重)中的最短路径。
问题分析
输入数据
- 给定一个
m x n
的网格,每个单元格要么可以通过(标记为0
),要么是障碍物(标记为1
)。 - 起点
a
和终点b
,我们需要找到从a
到b
的最短路径长度。
输出
返回路径长度(如果路径存在),或 -1 表示无法到达。
思路
-
广度优先搜索 (BFS):
- BFS 是一种层层扩展节点的搜索算法,适合用于寻找无权图的最短路径。
- 我们从起点
a
开始,把相邻的节点逐步加入到队列中,并逐层搜索,直到找到终点b
。
-
方向控制:
- 定义四个方向:上、下、左、右。
- 在每个位置,我们检查四个方向的相邻格子,判断它们是否可以通行。
- 如果前进方向发生变化,我们就更新路径长度,并继续扩展新的位置。
-
避免回到已访问位置:
- 使用一个访问集合
visited
,记录每个位置的访问状态,以避免重复访问相同的格子。
- 使用一个访问集合
-
障碍物处理:
- 在 BFS 中,如果遇到障碍物格子(值为
1
),我们跳过这个格子。
- 在 BFS 中,如果遇到障碍物格子(值为
代码实现
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 实现了最短路径搜索,并控制了“日”字形转向。通过方向判断,确保路径在遇到障碍物时能够绕行,且避免重复访问已探索的格子。