📅  最后修改于: 2023-12-03 14:58:33.355000             🧑  作者: Mango
在一个小镇有一些房子和一扇大门。
所有的房子和大门都位于 $m$ 行 $n$ 列的矩阵 $M$ 中。
大门位于第 $i_{gate}$ 行第 $j_{gate}$ 列,用字符 $g$ 表示。
所有房子用字符 $h$ 表示。我们用 $f_i$ 表示第 $i$ 间房子在矩阵中的位置,$f_i = (x_i, y_i)$。
你的任务是找到距离大门最远的房子,并输出它到大门的曼哈顿距离。
如果从大门无法到达某个房子,请输出 $-1$。
第一行包含三个整数 $m$, $n$, $k$,其中 $k$ 表示房子的数量。
接下来一行 $n$ 个字符,对应矩阵中第一行的字符。
接下来 $m-1$ 行,每行 $n$ 个字符,为矩阵中其余 $m-1$ 行字符。
其后 $k$ 行,每行两个整数 $x_i,y_i$,表示第 $i$ 间房子在矩阵中的位置。
矩阵中左上角的格子表示第 $1$ 行第 $1$ 列,第 $i$ 行第 $j$ 列对应的格子为 $(i,j)$,大门的位置为 $(i_{gate},j_{gate})$。$1\leq m,n,k\leq500$。
输出距离大门最远的房子与大门的曼哈顿距离,如果没有房子能够到达,请输出 $-1$。
输入:
3 4 2
g..h
....
hh..
2 2
3 3
输出:
3
本题要求我们求解大门和所有房子之间的曼哈顿距离,其中距离最大的那个房子即为所求。
本题的大门和房子的位置均给定,我们可以利用广度优先搜索求解大门与每个房子之间的最短距离。
曼哈顿距离即为两点之间横纵坐标差的绝对值之和,用 $d(x_1,y_1,x_2,y_2)$ 表示点 $(x_1,y_1)$ 和 $(x_2,y_2)$ 之间的曼哈顿距离,则有:
$$d(x_1,y_1,x_2,y_2)=|x_1-x_2|+|y_1-y_2|$$
可以直接套用广度优先搜索的模板求解。
广度优先搜索的时间复杂度为 $O(mnk)$,其中 $n,m,k$ 分别为矩阵的行数、列数和房子的数量。虽然符合要求,但当矩阵较大时,耗时较长。另一种更优秀的算法是多源广度优先搜索,其时间复杂度为 $O(mn)$,相较于广度优先搜索节省了 $k$ 的数量级。在本题条件下,多源广度优先搜索可以毫无压力地通过本题。
def bfs(m, n, gates, houses):
q = []
visited = [[False] * n for _ in range(m)]
max_distance = 0
for i, j in houses:
# 处理无法到达的情况
if m_distance((i, j), gates) == -1:
return -1
q.append((i, j, 0))
visited[i][j] = True
while q:
i, j, distance = q.pop(0)
max_distance = max(max_distance, m_distance((i, j), gates))
for x, y in [(i-1, j), (i+1, j), (i, j-1), (i, j+1)]:
if 0 <= x < m and 0 <= y < n and not visited[x][y]:
visited[x][y] = True
q.append((x, y, distance+1))
return max_distance
def m_distance(p1, p2):
"""计算曼哈顿距离"""
x1, y1 = p1
x2, y2 = p2
if (x1, y1) == (x2, y2):
return 0
if x1 != x2 and y1 != y2:
return -1
return abs(x1-x2) + abs(y1-y2)
m, n, k = map(int, input().split())
grid = [[c for c in input().strip()] for _ in range(m)]
houses = [(int(input().split()[0])-1, int(input().split()[1])-1) for _ in range(k)]
gates = (None, None)
for i in range(m):
for j in range(n):
if grid[i][j] == 'g':
gates = (i, j)
break
if gates[0] is not None:
break
print(bfs(m, n, gates, houses))