一、题目描述
在一个地图中(地图由n*n个区域组成),有部分区域被感染病菌。 感染区域每天都会把周围(上下左右)的4个区域感染。
请根据给定的地图计算,多少天以后,全部区域都会被感染。 如果初始地图上所有区域全部都被感染,或者没有被感染区域,返回-1
二、输入输出描述
输入描述
一行N*N个数字(只包含0,1,不会有其他数字,1<=N<200)表示一个地图,数字间用,分割,0表示未感染区域,1表示已经感染区域;
每N个数字表示地图中一行,输入数据共表示N行N列的区域地图。
例如输入1,0,1,0,0,0,1,0,1,表示地图
1,0,1
0,0,0
1,0,1
输出描述
一个整数,表示全部感染所需天数;特殊情况返回 -1。
三、示例
|----|--------------------------------|
| 输入 | 1,0,1,0,0,0,1,0,1 |
| 输出 | 2 |
| 说明 | 1天以后,地图中仅剩余中心点未被感染;2天以后,全部被感染。 |
|----|-------------------|
| 输入 | 1,1,1,1,1,1,1,1,1 |
| 输出 | -1 |
| 说明 | 全部都感染 |
四、解题思路
1. 核心思想
采用广度优先搜索(BFS) 模拟感染的层级扩散过程:
- BFS 的核心优势:按 "层" 遍历(每一层对应一天的扩散),能精准统计扩散的天数;
- 初始感染区域作为 BFS 的起始节点,每一轮处理队列中的所有节点(当天待扩散的区域),感染相邻健康区域并将新感染区域加入队列(下一天扩散),直到所有健康区域被感染或队列为空。
2. 问题本质分析
这个问题的核心是 **"无权图的最短路径 / 层级遍历"**:
- 二维矩阵可看作一个无权图,每个单元格是一个节点,相邻单元格(上下左右)是边;
- 初始感染节点到所有健康节点的 "最短路径长度" 的最大值,就是所有区域被感染的总天数;
- 特殊情况:无初始感染节点(全健康)或无健康节点(全感染),返回 - 1。
3. 核心逻辑
- 数据转换:将一维输入转为 n×n 二维矩阵,明确矩阵的空间结构;
- 初始化:
- 统计健康区域总数,判断特殊情况;
- 收集所有初始感染区域,作为 BFS 的起始队列;
- BFS 扩散:
- 按层处理队列中的感染区域,每处理一个区域,感染其四个方向的健康区域;
- 标记新感染区域的天数,并将其加入队列待后续扩散;
- 每轮扩散后更新剩余健康区域数量,直到健康区域为 0;
- 结果计算:返回最大扩散天数(需修正计数偏差)。
4. 步骤拆解
- 输入处理:读取逗号分隔的字符串,转为一维整数列表;
- 矩阵转换 :
- 计算矩阵边长
n = √(列表长度); - 将一维列表转为 n×n 二维矩阵;
- 计算矩阵边长
- 初始化参数 :
- 队列存储初始感染区域坐标;
- 统计健康区域数量
healthy; - 定义上下左右四个方向的偏移量;
- 特殊情况判断 :若
healthy=0(全感染)或healthy=n×n(全健康),返回 - 1; - BFS 扩散循环 :
- 取出队列中的感染区域坐标,计算下一轮扩散天数
day; - 遍历四个方向,校验新坐标的边界和健康状态;
- 感染健康区域:
healthy--,标记感染天数,将新坐标加入队列; - 直到队列为空或
healthy=0;
- 取出队列中的感染区域坐标,计算下一轮扩散天数
- 结果返回 :返回
day-1(修正天数计数偏差)。
五、代码实现
java
import java.util.*;
public class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
String input = scanner.nextLine();
List<Integer> map = new ArrayList<>();
int pos = 0;
String token;
while ((pos = input.indexOf(",")) != -1) { // 将输入字符串转换为一维数组
token = input.substring(0, pos);
map.add(Integer.parseInt(token));
input = input.substring(pos + 1);
}
map.add(Integer.parseInt(input));
System.out.println(getInfectionDays(map)); // 输出感染天数
}
public static int getInfectionDays(List<Integer> map) {
int n = (int) Math.sqrt(map.size());
int[][] matrix = new int[n][n]; // 将一维数组转换为二维矩阵
Queue<int[]> q = new LinkedList<>(); // 用队列存储感染区域
int healthy = 0; // 记录未感染区域数量
int[][] offsets = {{-1, 0}, {1, 0}, {0, -1}, {0, 1}}; // 记录四个方向的偏移量
for (int i = 0; i < n * n; i++) {
int x = i / n;
int y = i % n;
matrix[x][y] = map.get(i); // 将一维数组转换为二维矩阵
if (matrix[x][y] == 1) {
q.offer(new int[]{x, y}); // 将感染区域加入队列
} else {
healthy++; // 计算未感染区域数量
}
}
if (healthy == 0 || healthy == n * n) { // 判断特殊情况
return -1;
}
int day = 0; // 记录感染天数
while (!q.isEmpty() && healthy > 0) { // 当队列不为空且还有未感染区域时,进行循环
int[] tmp = q.poll(); // 取出队首元素
int x = tmp[0], y = tmp[1]; // 获取队首元素的坐标
day = matrix[x][y] + 1; // 记录感染天数
for (int[] offset : offsets) { // 遍历四个方向
int newX = x + offset[0]; // 新的横坐标
int newY = y + offset[1]; // 新的纵坐标
if (newX >= 0 && newX < n && newY >= 0 && newY < n && matrix[newX][newY] == 0) { // 判断边界和未感染区域
healthy--; // 未感染区域数量减一
matrix[newX][newY] = day; // 标记该区域已感染
q.offer(new int[]{newX, newY}); // 将该区域加入队列
}
}
}
return day - 1; // 返回感染天数
}
}