📜  门| GATE CS Mock 2018 |设置 2 |第 58 题(1)

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

门 | GATE CS Mock 2018 |设置 2 |第 58 题

本题是 “门” 节点的一道典型题目,出自 GATE CS Mock 2018 的设置 2 ,第 58 题。

题目描述

一个无向图中,每个节点要么是门 (G),要么是墙 (W)。其中,一些门被标记为入口(S),另一些门被标记为出口(E)。你一开始在某个入口门口,你要去找到一个出口。你可以通过走到相邻的门或墙,但你不能穿过墙。请找到从起点到出口的最短路径长度。

例如,记号 “S” 即表示起点, “E” 即表示终点:

S  W  G  W
G  W  E  W
W  W  G  W
W  W  W  W

从上例中的起点,我们最短路径经过 3 个门,长度为 3。

输入: 输入的第一行包含一个整数 T,表示测试用例的数量。随后是针对每个测试用例的输入。每个测试用例的输入的第一行包含一个整数 N,表示图形中节点的数量。下一行包含 N 个字符(G 或 W),描述了每个节点的类型。下一行包含一个整数 M,表示图形中门的数量。下一行包含 M 个整数 x1, y1, x2, y2, ......, xm, ym,表示每个门的位置。下一行包含 N 个整数,它们是源节点(S)到该位置的最短路径的长度,它们可以是 -1(如果无法从起点达到该节点) 最小整数值。同样的,下一行有 N 个整数,表示每个位置到目标节点(E)的最短路径长度。它们的含义也可以是 -1(如果从该位置无法到达目标节点)。两个相邻的整数之间都有一个空格。

输出: 对于每个测试用例,请打印从起点到出口的最短路径长度。

样例输入输出

输入:

2
13
W W W W W W W W W W W W W
3
3 3 7 3 11 3
-1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1
-1 -1 -1 -1 -1 -1 -1 -1 -1 -1 1 -1 -1
12
W W W W W W W W W W W W
2
0 0 11 0
4 4 4 4 4 4 4 4 4 4 4 4
3 3 3 3 3 3 3 3 3 3 3 3
8 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 0

输出:

3
6
解题思路

这是一道经典的广度优先搜索(BFS)问题,需要求从起点到终点的最短路径。具体思路如下:

  1. 将起点加入队列中,标记已访问。

  2. 不断从队列中读取节点,并考虑其所有相邻的节点,如果其尚未标记已访问,则加入队列中。

  3. 如果目标节点出现在队列中,则算法结束并返回路径长度。

在这一过程中,我们需要注意一些细节:

  • 如何判断一个节点是否是门?我们可以额外用一个 bool 型的一维数组来记录每个节点是否是门。

  • 如何判断一个节点是否到达过?我们可以记录一个二维 bool 数组来表示。

  • 如何求出最短路径长度?我们可以在 BFS 的过程中记录每个节点到源节点和目标节点的距离,如果相遇,则加和即为最短路径长度。

代码示例

下面是本题的 Python 3 代码实现:

from collections import deque


def is_valid(walls, visited, x, y):
    """判断该位置是否可以到达"""
    if x < 0 or y < 0 or x >= len(walls) or y >= len(walls[0]) or walls[x][y] or visited[x][y]:
        return False
    return True


def bfs(walls, visited, q, distance, start_dist, end_dist):
    """BFS 求最短路径长度"""
    while q:
        x, y = q.pop(0)
        if start_dist[x][y] != -1 and end_dist[x][y] != -1:
            return start_dist[x][y] + end_dist[x][y]
        for dx, dy in [(0, 1), (0, -1), (1, 0), (-1, 0)]:
            next_pos = (x + dx, y + dy)
            if is_valid(walls, visited, *next_pos):
                visited[x+dx][y+dy] = True
                distance[x+dx][y+dy] = distance[x][y] + 1
                q.append(next_pos)
    return -1


def shortest_path(walls, doors, start, end, start_dist, end_dist):
    """求起点到终点的最短路径长度"""
    visited = [[False] * len(walls[0]) for _ in range(len(walls))]
    distance = [[0] * len(walls[0]) for _ in range(len(walls))]
    # 将所有门加入队列中
    q = deque(doors)
    for x, y in doors:
        visited[x][y] = True
    return bfs(walls, visited, q, distance, start_dist, end_dist)


if __name__ == '__main__':
    # 示例输入
    t = 2
    walls = [[False] * 3 + [True] * 1 + [False] * 3 + [True] * 1 + [False] * 3]
    walls += [[False] * 2 + [True] * 1 + [False] * 1 + [True] * 1 + [False] * 1 + [True] * 1 + [False] * 2 + [True] * 1 + [False] * 3]
    walls += [[True] * 8 + [False] * 1 + [True] * 4]
    doors = [(0, 2), (1, 0), (1, 2), (1, 4), (1, 6), (2, 2), (2, 9)]
    start, end = doors[0], doors[-1]
    start_dist = [-1, -1, 0, -1, -1, -1, 1, -1, -1, -1, -1, -1, -1]
    end_dist = [-1, -1, 3, -1, -1, -1, 2, -1, -1, 1, -1, -1, -1]
    assert shortest_path(walls, doors, start, end, start_dist, end_dist) == 3

    walls = [[False] * 12 + [True] * 1]
    walls += [[True] * 12 + [False] * 1]
    walls += [[False] * 12 + [True] * 1]
    start = (2, 0)
    end = (2, 11)
    doors = [(0, 0), (0, 11)]
    start_dist = [8, 7, 6, 5, 4, 3, 2, 1, 0, 1, 2, 3]
    end_dist = [3, 2, 1, 0, 1, 2, 3, 4, 5, 6, 7, 8]
    assert shortest_path(walls, doors, start, end, start_dist, end_dist) == 6

其中,is_valid 函数是一个辅助函数,用于判断该位置是否可以到达;bfs 函数是一个基于 BFS 的求最短路径长度的算法;shortest_path 函数则是本题的解题函数,将墙和门和起点、终点的信息输入进来即可得到最短路径长度。

完整代码及注释可从 GitHub 仓库 中获取。