一、题目描述
存在一个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. 步骤拆解
-
输入解析与初始化:
- 解析输入参数,得到矩阵尺寸
m×n和两个扩散起点(i,j)、(k,l)。 - 初始化矩阵(标记起点为已扩散)、未扩散位置计数
count、多源 BFS 队列(加入两个起点)。
- 解析输入参数,得到矩阵尺寸
-
多源 BFS 循环扩散:
- 初始化天数
day=0,进入循环(队列非空且仍有未扩散位置):- 创建临时队列
newQueue,存储当天新扩散的位置。 - 遍历当前队列中的所有扩散源,检查其四个方向的相邻位置:
- 若位置合法且未扩散,标记为已扩散、加入
newQueue、减少count。
- 若位置合法且未扩散,标记为已扩散、加入
- 将队列更新为
newQueue(次日的扩散源),天数day加 1。
- 创建临时队列
- 初始化天数
-
结果输出:
- 循环结束后,返回总天数
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;
}
}