1、问题
1.1 问题描述
在一个封闭的房间中,排列着 n 行 m 列的座位,每个座位间距为 1 米。房间中每个座位上都坐着一人,部分人戴了口罩,部分人未戴口罩。已知房间中有一位已经感染病毒的人,病毒可以每秒向相邻座位传播 1 米。对于戴口罩的人,病毒需要两秒才能成功感染,或者需要在一秒内从两个相邻的感染者处同时被感染。设计一个算法,计算病毒感染房间内所有人所需的最短时间。
1.2 测试样例
样例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
样例3:
输入:
row_n = 5, column_m = 5, seats = [[1, 1, 1, 1, 1], [1, 0, 0, 0, 1], [1, 0, 1, 0, 1], [1, 0, 0, 0, 1], [1, 1, 1, 1, 1]], patient = [3, 3]
输出:7
2、思路
本题的核心在于模拟病毒在房间中的传播过程。我们需要计算病毒感染房间内所有人所需的最短时间。病毒的传播规则如下:
- 未戴口罩的人:病毒可以在1秒内感染相邻的未戴口罩的人。
- 戴口罩的人:病毒需要2秒才能成功感染,或者需要在一秒内从两个相邻的感染者处同时被感染。
为了实现这一过程,我们可以使用广度优先搜索(BFS)算法。BFS算法能够有效地模拟病毒的传播过程,并且能够保证在每一秒内处理所有当前感染者的相邻座位。
具体步骤如下:
- 初始化:将初始感染者的位置加入队列,并将该位置标记为已感染(值设为-1)。
- BFS传播:在每一秒内,处理队列中的所有当前感染者,尝试向四个方向(上、下、左、右)传播病毒。
- 更新状态:对于每个相邻的座位,如果该座位是未感染的(值为0),则将其加入队列;如果该座位是戴口罩的(值为1),则减少其值。
- 终止传播:当队列中的元素数量等于矩阵中的总座位数时,表示所有座位都已被感染,结束循环。
2.1 初始化
首先,我们需要初始化一些变量,包括时间计数器 time,感染源的坐标 x 和 y,以及一个队列 q 用于存储当前感染的座位。此外,在开始传播病毒之前,我们需要检查感染源是否在矩阵范围内。如果不在范围内,直接返回时间 0
time = 0 # 初始化时间计数器
x = patient[0] # 获取感染源的行坐标
y = patient[1] # 获取感染源的列坐标
# 检查感染源是否在矩阵范围内
if x < 0 or x > row_n - 1 or y < 0 or y > column_m - 1:
return time # 如果不在范围内,直接返回0
# 初始化队列,将感染源加入队列
q = [[x, y]]
# 将感染源位置的值设为-1,表示已感染
seats[x][y] = -1
2.2 BFS传播
使用BFS算法来模拟病毒的传播过程。每次从队列中取出一个座位,检查其四个相邻座位是否可以被感染。如果相邻座位是未感染的(值为 0),则将其加入队列;如果相邻座位是戴口罩的(值为 1),则减少其值。
while len(q) != row_n * column_m:
old_len = len(q) # 记录当前队列的长度
for i in range(old_len):
now_x = q[i][0] # 获取当前处理元素的行坐标
now_y = q[i][1] # 获取当前处理元素的列坐标
# 尝试向四个方向传播病毒
spread(now_x - 1, now_y, row_n, column_m, seats, q)
spread(now_x + 1, now_y, row_n, column_m, seats, q)
spread(now_x, now_y - 1, row_n, column_m, seats, q)
spread(now_x, now_y + 1, row_n, column_m, seats, q)
time += 1 # 每轮传播结束后,时间加1
2.3 更新状态
通过spread函数进行状态更新,它会检查当前位置 (x, y) 是否在矩阵范围内,并根据座位状态进行相应的处理。在每轮传播以及状态更新结束后,时间计数器 time 加 1。
def spread(x, y, n, m, seats, queue):
# 检查当前位置是否在矩阵范围内
if 0 <= x <= n - 1 and 0 <= y <= m - 1:
# 如果当前位置是未感染的(值为0),则将其加入队列
if seats[x][y] == 0:
queue.append([x, y])
# 如果当前位置不是感染源(值不为-1),则减少其值
if seats[x][y] != -1:
seats[x][y] -= 1
2.4 终止传播
当队列中的元素数量等于矩阵中的总座位数时,表示所有座位都已被感染,那么停止传播。
3、代码
整体代码如下:
def spread(x, y, n, m, seats, queue):
# 检查当前位置是否在矩阵范围内
if 0 <= x <= n - 1 and 0 <= y <= m - 1:
# 如果当前位置是未感染的(值为0),则将其加入队列
if seats[x][y] == 0:
queue.append([x, y])
# 如果当前位置不是感染源(值不为-1),则减少其值
if seats[x][y] != -1:
seats[x][y] -= 1
def solution(row_n, column_m, seats, patient):
time = 0 # 初始化时间计数器
x = patient[0] # 获取感染源的行坐标
y = patient[1] # 获取感染源的列坐标
# 检查感染源是否在矩阵范围内
if x < 0 or x > row_n - 1 or y < 0 or y > column_m - 1:
return time # 如果不在范围内,直接返回0
# 初始化队列,将感染源加入队列
q = [[x, y]]
# 将感染源位置的值设为-1,表示已感染
seats[x][y] = -1
# 当队列中的元素数量不等于矩阵中的总座位数时,继续循环
while len(q) != row_n * column_m:
old_len = len(q) # 记录当前队列的长度
for i in range(old_len):
now_x = q[i][0] # 获取当前处理元素的行坐标
now_y = q[i][1] # 获取当前处理元素的列坐标
# 尝试向四个方向传播病毒
spread(now_x - 1, now_y, row_n, column_m, seats, q)
spread(now_x + 1, now_y, row_n, column_m, seats, q)
spread(now_x, now_y - 1, row_n, column_m, seats, q)
spread(now_x, now_y + 1, row_n, column_m, seats, q)
time += 1 # 每轮传播结束后,时间加1
return time # 返回总时间
4、运行结果
可以看到提交通过,说明我们的方法和代码都没有问题。
5、总结分析
5.1 时间复杂度
- 初始化:初始化时间计数器和队列的时间复杂度为
O(1)。 - BFS传播(包括了更新状态):每次传播时,我们需要遍历当前队列中的所有元素,并检查其相邻座位。在最坏情况下,每个座位都需要被检查一次,因此时间复杂度为
O(n * m)。
5.2 空间复杂度
- 队列:队列的空间复杂度为
O(n * m),因为最坏情况下所有座位都需要被存储在队列中。 - 矩阵:矩阵的空间复杂度为
O(n * m)。
该算法通过BFS模拟了病毒的传播过程,能够有效地计算出病毒感染房间内所有人所需的最短时间。算法的时间复杂度和空间复杂度均为 O(n * m)(其中 n 和 m 分别是矩阵的行数和列数),适用于大多数情况下的计算。