问题描述
在一个封闭的房间中,有 n 行 m 列的座位,每个座位上都坐着一个人,部分人戴了口罩,部分人未戴口罩。已知房间中有一位已经感染病毒的人,病毒可以每秒向相邻座位传播 1 米。对于戴口罩的人,病毒需要两秒才能成功感染,或者需要在一秒内从两个相邻的感染者处同时被感染。我们需要设计一个算法,计算病毒感染房间内所有人所需的最短时间。
测试样例
样例1:
输入:
row_n = 4, column_m = 4, seats = [[0, 1, 1, 1], [1, 0, 1, 0], [1, 1, 1, 1], [0, 0, 0, 1]], patient = [2, 2]
输出:6
样例2:
输入:
row_n = 3, column_m = 3, seats = [[0, 0, 0], [0, 1, 0], [0, 0, 0]], patient = [2, 2]
输出:4
算法设计
这个问题可以通过广度优先搜索(BFS)算法来解决。我们使用一个队列来存储待处理的座位和对应的传播时间,同时使用一个二维数组 time 来记录每个座位被感染的时间。
算法步骤
-
初始化:将感染者的位置加入队列,并设置其感染时间为 0。同时,初始化
time数组,所有元素设为 -1,表示未被感染。 -
定义方向:定义四个方向的偏移量,用于遍历相邻座位。
-
BFS 循环:当队列不为空时,执行以下操作:
- 取出队列中的第一个元素(当前座位和感染时间)。
- 遍历当前座位的四个相邻座位。
- 对于每个相邻座位,如果该座位上的人未戴口罩(
seats[nr][nc] == 0),则直接感染,感染时间为当前时间加 1。 - 如果该座位上的人戴了口罩(
seats[nr][nc] == 1),则需要检查是否在一秒内可以从两个相邻的感染者处同时被感染,或者需要两秒才能感染。这通过检查time数组来实现。
-
排序队列:因为感染的优先性,这里对队列进行排序,以便于我们按照病毒感染的时间顺序进行遍历(
可以使用优先队列代替)。 -
返回结果:当队列为空时,遍历
time数组,找到最大的感染时间,即为病毒传播到所有人所需的最短时间。
代码
主函数中有一个越界的案例,但是在代码中我并没有考虑越界的问题
from collections import deque
def solution(row_n, column_m, seats, patient):
# 初始化队列和时间数组
# 队列用于存储待处理的座位和对应的传播时间
queue = deque([(patient[0], patient[1], 0)]) # (row, column, time)
# 时间数组用于记录每个座位被感染的时间,初始值为-1表示未被感染
time = [[-1] * column_m for _ in range(row_n)]
# 感染者的位置时间设置为0
time[patient[0]][patient[1]] = 0
# 定义四个方向的偏移量,用于遍历相邻座位
directions = [(-1, 0), (1, 0), (0, -1), (0, 1)]
# BFS 实现
while queue:
# 取出队列中的第一个元素(当前座位和感染时间)
r, c, t = queue.popleft()
# 遍历当前座位的四个相邻座位
for dr, dc in directions:
nr, nc = r + dr, c + dc
# 检查相邻座位是否在房间内
if 0 <= nr < row_n and 0 <= nc < column_m:
# 如果相邻座位上的人未戴口罩且未被感染
if seats[nr][nc] == 0 and time[nr][nc] == -1:
# 1秒感染,更新时间并加入队列
time[nr][nc] = t + 1
queue.append((nr, nc, t + 1))
elif seats[nr][nc] == 1:
# 如果相邻座位上的人戴了口罩
# 检查是否在一秒内可以从两个相邻的感染者处同时被感染,或者需要两秒才能感染
if t + 2 < time[nr][nc] or time[nr][nc] == -1:
num = 0
# 检查戴口罩的人周围是否有两个已感染的座位
for dr, dc in directions:
if 0 <= nr + dr < row_n and 0 <= nc + dc < column_m and time[nr + dr][nc + dc] != -1 and time[nr + dr][nc + dc] <= t:
num += 1
# 如果有两个相邻的感染者,则1秒感染,否则2秒感染
if num >= 2:
time[nr][nc] = t + 1
queue.append((nr, nc, t + 1))
else:
time[nr][nc] = t + 2
queue.append((nr, nc, t + 2))
# 根据时间对队列进行排序,优先处理感染时间较短的座位
sorted_list = sorted(list(queue), key=lambda x: x[-1])
# 将排序后的列表转换回deque
queue = deque(sorted_list)
# 遍历时间数组,找到最大的感染时间,即为病毒传播到所有人所需的最短时间
return max(max(row) for row in time)
复杂度分析
时间复杂度
-
初始化队列和时间数组:
- 初始化队列的时间复杂度为 O(1)。
- 初始化时间数组的时间复杂度为 O(n * m),其中 n 是行数,m 是列数。
-
BFS 循环:
- 每个座位最多被访问两次(一次是它被感染时,一次是它作为感染源时)。
- BFS 循环中的每个座位都需要检查四个方向,因此每个座位的检查时间复杂度为 O(4)。
- 总的时间复杂度为 O(2 * n * m * 4) = O(8 * n * m)。
-
队列排序:
- 每次循环结束时,队列中的元素都会被排序一次。
- 队列的最大长度为 n * m,因此排序的时间复杂度为 O((n * m) * log(n * m))。
综合以上,算法的总体时间复杂度为 O(8 * n * m + (n * m) * log(n * m))。由于排序操作的复杂度高于常数倍的 BFS 操作,因此时间复杂度可以简化为 O((n * m) * log(n * m))。
空间复杂度
-
队列:
- 队列的最大长度为 n * m,因此空间复杂度为 O(n * m)。
-
时间数组:
- 时间数组是一个 n * m 的二维数组,因此空间复杂度为 O(n * m)。
-
方向数组:
- 方向数组是固定的,空间复杂度为 O(1)。
综合以上,算法的总体空间复杂度为 O(n * m)。