题目解析 病毒在封闭空间的传播时间 | 豆包MarsCode AI刷题

164 阅读5分钟

问题描述

在一个封闭的房间中,有 nm 列的座位,每个座位上都坐着一个人,部分人戴了口罩,部分人未戴口罩。已知房间中有一位已经感染病毒的人,病毒可以每秒向相邻座位传播 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 来记录每个座位被感染的时间。

算法步骤

  1. 初始化:将感染者的位置加入队列,并设置其感染时间为 0。同时,初始化 time 数组,所有元素设为 -1,表示未被感染。

  2. 定义方向:定义四个方向的偏移量,用于遍历相邻座位。

  3. BFS 循环:当队列不为空时,执行以下操作:

    • 取出队列中的第一个元素(当前座位和感染时间)。
    • 遍历当前座位的四个相邻座位。
    • 对于每个相邻座位,如果该座位上的人未戴口罩(seats[nr][nc] == 0),则直接感染,感染时间为当前时间加 1。
    • 如果该座位上的人戴了口罩(seats[nr][nc] == 1),则需要检查是否在一秒内可以从两个相邻的感染者处同时被感染,或者需要两秒才能感染。这通过检查 time 数组来实现。
  4. 排序队列:因为感染的优先性,这里对队列进行排序,以便于我们按照病毒感染的时间顺序进行遍历(可以使用优先队列代替)。

  5. 返回结果:当队列为空时,遍历 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)
    

复杂度分析

时间复杂度

  1. 初始化队列和时间数组

    • 初始化队列的时间复杂度为 O(1)。
    • 初始化时间数组的时间复杂度为 O(n * m),其中 n 是行数,m 是列数。
  2. BFS 循环

    • 每个座位最多被访问两次(一次是它被感染时,一次是它作为感染源时)。
    • BFS 循环中的每个座位都需要检查四个方向,因此每个座位的检查时间复杂度为 O(4)。
    • 总的时间复杂度为 O(2 * n * m * 4) = O(8 * n * m)。
  3. 队列排序

    • 每次循环结束时,队列中的元素都会被排序一次。
    • 队列的最大长度为 n * m,因此排序的时间复杂度为 O((n * m) * log(n * m))。

综合以上,算法的总体时间复杂度为 O(8 * n * m + (n * m) * log(n * m))。由于排序操作的复杂度高于常数倍的 BFS 操作,因此时间复杂度可以简化为 O((n * m) * log(n * m))。

空间复杂度

  1. 队列

    • 队列的最大长度为 n * m,因此空间复杂度为 O(n * m)。
  2. 时间数组

    • 时间数组是一个 n * m 的二维数组,因此空间复杂度为 O(n * m)。
  3. 方向数组

    • 方向数组是固定的,空间复杂度为 O(1)。

综合以上,算法的总体空间复杂度为 O(n * m)。