📜  门|门 IT 2008 |第 57 题(1)

📅  最后修改于: 2023-12-03 15:28:48.546000             🧑  作者: Mango

门|门 IT 2008 |第 57 题

题目描述

给定一个 n 行 m 列的地图,地图中用 0 表示空地,用 1 表示障碍物。在地图中选择一个起点和一个终点,你需要求出从起点出发,最少需要经过几个障碍物才能到达终点。

输入格式

输入共 n+2 行,第一行包含两个整数 n,m,表示地图的大小。

接下来 n 行,每行包含 m 个 0 或 1,表示地图。

最后一行包含四个整数 x1,y1,x2,y2,表示起点坐标为 (x1,y1),终点坐标为 (x2,y2)。地图的行和列下标都从 1 开始。

输出格式

输出一个整数,表示从起点出发,最少需要经过几个障碍物才能到达终点。如果无法到达终点,输出 -1。

示例
输入示例
5 5
0 1 0 0 0
0 1 0 1 0
0 0 0 0 0
0 1 1 1 0 
0 0 0 1 0
1 2 4 5
输出示例
7
解题思路

本题为典型的图论--最短路问题,可以使用宽度优先搜索(BFS)来解决。从起点开始寻找目标点,采用类似于树的层次遍历的方式,先遍历到的点距离源点的步数更小,因此在遍历过程中,维护一个当前点距离起点的步数变量,以及一个队列,每次将当前点入队并标记访问过,同时更新离起点的距离和是否到达终点。

具体实现过程中需要注意以下几点:

  1. 如何记录到达终点时的步数,可以给队列中的每个点额外加一个步数的属性。
  2. 如何判断无法到达终点的情况,可以在搜索完成后判断终点是否被访问过,若未被访问过则说明无法到达终点。
代码实现
def bfs(n, m, graph, x1, y1, x2, y2):
    queue = [(x1, y1, 0)]  # 初始化队列,从起点出发距离为0
    visited = [[False] * m for _ in range(n)]  # 初始化所有点为未访问
    visited[x1][y1] = True  # 标记起点为已访问
    while queue:
        x, y, step = queue.pop(0)
        for dx, dy in [(0, 1), (0, -1), (1, 0), (-1, 0)]:  # 遍历上下左右4个点
            nx, ny = x+dx, y+dy
            if 0 <= nx < n and 0 <= ny < m and not visited[nx][ny] and not graph[nx][ny]:
                # 若该点在图内、未访问过、是空地,则进入队列并标记为已访问
                visited[nx][ny] = True
                queue.append((nx, ny, step+1))
                if nx == x2 and ny == y2:  # 若到达目标点,则返回答案
                    return step+1
    return -1  # 若无法到达目标点,则返回-1

if __name__ == '__main__':
    n, m = map(int, input().split())
    graph = [list(map(int, input().split())) for _ in range(n)]
    x1, y1, x2, y2 = map(int, input().split())
    ans = bfs(n, m, graph, x1-1, y1-1, x2-1, y2-1)  # 减1是因为题目中的行和列下标从1开始
    print(ans)
时间复杂度

BFS遍历完所有可以到达的点,时间复杂度即为图上遍历所需的时间复杂度,为 $O(nm)$。

空间复杂度

队列中最多存储 $O(nm)$ 个点,visited数组同样需要存储所有点的访问状态,因此空间复杂度为 $O(nm)$。

总体来说,该算法的时间和空间复杂度都十分优秀,适用于解决规模较小的最短路问题。