矩阵扩散问题

一、题目描述

存在一个m×n的二维数组,其成员取值范围为0或1。其中值为1的成员具备扩散性,每经过1S,将上下左右值为0的成员同化为1。

二维数组的成员初始值都为0,将第[i,j]和[k,l]两个个位置上元素修改成1后,求矩阵的所有元素变为1需要多长时间。

二、输入输出描述

1. 输入描述

  • 输入为一行整数,依次为:m(行数)、n(列数)、i(第一个扩散点行坐标)、j(第一个扩散点列坐标)、k(第二个扩散点行坐标)、l(第二个扩散点列坐标);
  • 约束:m、n ≤ 1024,坐标均为合法矩阵索引(从 0 开始)。

2. 输出描述

  • 输出一个整数,表示矩阵所有元素变为 1 所需的秒数。

三、示例

|----|-------------------------------------------------------------------------------------------------------------------------|
| 输入 | 4,4,0,0,3,3 |
| 输出 | 3 |
| 说明 | 输入数据中的前2个数字表示这是一个4*4的矩阵; 中间两个数字表示一个初始扩散点位置为0,0; 最后2个数字表示另一个扩散点位置为3,3。 给出的样例是一个简单模型,初始点在对角线上,达到中间的位置分别为3次迭代,即3秒。所以输出为3。 |

四、解题思路

1. 核心思想

采用多源广度优先搜索(多源 BFS) 模拟同步扩散过程:将两个起点同时纳入 BFS 队列,按 "天" 为单位逐层扩散(每天所有当前扩散源同步向外扩展一格),直到覆盖整个矩阵,扩散的总天数即为答案。

2. 问题本质分析

问题本质是最短路径的多源同步扩展问题

  • 扩散过程等价于 "从两个起点出发,同步向四周扩展,求覆盖所有节点的最少步数"。
  • 传统单源 BFS 适用于 "单个起点找最短路径",而多源 BFS 通过将多个起点同时入队,实现 "同步扩展",保证每一步(每一天)都是所有当前扩散源的共同扩展,符合 "同时扩散" 的场景要求。
  • 核心约束:扩散是 "同步" 的,即每个扩散源在同一天内完成向四周的扩展,而非依次扩展。
3. 核心逻辑
  • 多源初始化:将两个扩散起点同时加入 BFS 队列,保证初始状态下两个起点同步开始扩散。
  • 分层扩散:以 "天" 为单位分层处理队列(每层对应一天),当天所有扩散源扩展出的新位置作为下一天的扩散源,确保扩散的同步性。
  • 终止条件:当所有位置被扩散覆盖(count=0)或无新扩散源(队列空)时停止,此时的天数即为总时间。
4. 步骤拆解
  1. 输入解析与初始化

    • 解析输入参数,得到矩阵尺寸 m×n 和两个扩散起点 (i,j)(k,l)
    • 初始化矩阵(标记起点为已扩散)、未扩散位置计数 count、多源 BFS 队列(加入两个起点)。
  2. 多源 BFS 循环扩散

    • 初始化天数 day=0,进入循环(队列非空且仍有未扩散位置):
      1. 创建临时队列 newQueue,存储当天新扩散的位置。
      2. 遍历当前队列中的所有扩散源,检查其四个方向的相邻位置:
        • 若位置合法且未扩散,标记为已扩散、加入 newQueue、减少 count
      3. 将队列更新为 newQueue(次日的扩散源),天数 day 加 1。
  3. 结果输出

    • 循环结束后,返回总天数 day,即为覆盖整个矩阵的最少扩散时间。

补充说明

  • 多源 BFS 的优势:相比 "单源 BFS 分别计算两个起点的扩散时间再合并",多源 BFS 直接模拟同步扩散,无需额外处理时间叠加,更贴合 "同时扩散" 的场景,且时间复杂度为 O (m×n)(每个位置仅入队一次),效率最优。
  • 边界情况:若两个起点已覆盖整个矩阵(m×n=2),则 count=0,循环不执行,返回 day=0(无需扩散)。

五、代码实现

java 复制代码
import java.util.Arrays;
import java.util.LinkedList;
import java.util.Scanner;

public class Main {
  public static void main(String[] args) {
    Scanner sc = new Scanner(System.in);

    Integer[] arr =
        Arrays.stream(sc.next().split(",")).map(Integer::parseInt).toArray(Integer[]::new);

    System.out.println(getResult(arr[0], arr[1], arr[2], arr[3], arr[4], arr[5]));
  }

  /**
   * @param m m m×n的二维数组
   * @param n n m×n的二维数组
   * @param i 扩散点位置为i,j
   * @param j 扩散点位置为i,j
   * @param k 扩散点位置为k,l
   * @param l 扩散点位置为k,l
   * @return 扩散所有点需要的时间
   */
  public static int getResult(int m, int n, int i, int j, int k, int l) {
    int[][] matrix = new int[m][n];
    matrix[i][j] = 1;
    matrix[k][l] = 1;

    // count记录未被扩散的点的数量
    int count = m * n - 2;

    // 多源BFS实现队列
    LinkedList<int[]> queue = new LinkedList<>();
    queue.addLast(new int[] {i, j});
    queue.addLast(new int[] {k, l});

    // 上下左右偏移量
    int[][] offsets = {{1, 0}, {-1, 0}, {0, 1}, {0, -1}};

    int day = 0;
    // 如果扩散点没有了,或者所有点已被扩散,则停止循环
    while (queue.size() > 0 && count > 0) {
      LinkedList<int[]> newQueue = new LinkedList<>();

      for (int[] pos : queue) {
        int x = pos[0];
        int y = pos[1];

        for (int[] offset : offsets) {
          int newX = x + offset[0];
          int newY = y + offset[1];

          if (newX >= 0 && newX < m && newY >= 0 && newY < n && matrix[newX][newY] == 0) {
            // 将点被扩散的时间记录为该点的值
            matrix[newX][newY] = 1;
            // 被扩散到的点将变为新的扩散源
            newQueue.addLast(new int[] {newX, newY});
            // 未被扩散点的数量--
            count--;
          }
        }
      }

      queue = newQueue;
      day++;
    }

    return day;
  }
}
相关推荐
独自破碎E1 小时前
力场重叠问题
java·开发语言·算法
free-elcmacom1 小时前
机器学习入门<5>支持向量机形象教学:寻找最安全的“三八线”,人人都能懂的算法核心
人工智能·python·算法·机器学习·支持向量机
组合缺一1 小时前
Solon AI 开发学习16 - generate - 生成模型(图、音、视)
java·人工智能·学习·ai·llm·solon
谷哥的小弟1 小时前
Spring Framework源码解析——AnnotationAwareOrderComparator
java·后端·spring·源码
八月瓜科技1 小时前
八月瓜科技参与“数据要素驱动产业升级”活动,分享【数据赋能科技创新全链条】
java·大数据·人工智能·科技·机器人·程序员创富
谷哥的小弟1 小时前
Spring Framework源码解析——StringUtils
java·后端·spring·源码
G_whang1 小时前
win10环境下jdk17下载安装及环境配置
java
非情剑1 小时前
Java-Executor线程池配置-案例2
android·java·开发语言