994. 腐烂的橘子:带有变量控制的矩阵中的广度优先搜索
在给定的 m x n 网格 grid 中,每个单元格可以有以下三个值之一:
- 值 0 代表空单元格;
- 值 1 代表新鲜橘子;
- 值 2 代表腐烂的橘子。
每分钟,腐烂的橘子 周围 4 个方向上相邻 的新鲜橘子都会腐烂。
返回 直到单元格中没有新鲜橘子为止所必须经过的最小分钟数。如果不可能,返回 -1 。
示例 1:
输入:grid = [[2,1,1],[1,1,0],[0,1,1]]
输出:4
示例 2:
输入:grid = [[2,1,1],[0,1,1],[1,0,1]]
输出:-1
解释:左下角的橘子(第 2 行, 第 0 列)永远不会腐烂,因为腐烂只会发生在 4 个正向上。
示例 3:
输入:grid = [[0,2]]
输出:0
解释:因为 0 分钟时已经没有新鲜橘子了,所以答案就是 0 。
广度优先搜索解法:
新鲜的橘子变为腐烂,一定是由其上下左右任一相邻的腐烂橘子所造成的后果,即从腐烂的橘子(矩阵中值为2)的位置出发逐步前进,直到所有的橘子均腐烂或者腐烂的橘子已经没有相邻的新鲜橘子进行下一步的操作。为多源广度优先搜索问题,使用队列辅助完成。
首先仍然遍历输入矩阵,将所有腐烂橘子的位置(矩阵值为2)入队,表示广度优先搜索的各个起点,然后从每个腐烂的橘子出发,寻找其相邻的新鲜橘子(矩阵值为1)进行处理,将新鲜橘子对应的矩阵值置为 2 并将新的腐烂橘子的位置入队。
虽然同样为多元广度优先搜索,本题与 542. 01 矩阵 和 1765. 地图中的最高点 的不同在于,我们进行广度优先搜索过程中需要按照每一轮次进行(每一轮在本题中表示每一分钟),而不是每个位置进行。二者本质上进行操作的流程基本相同,但本题不要求我们返回最终矩阵的结果,而是要求返回所有橘子是否都会腐烂及其所需时间,因此按照每个轮次遍历操作(现有队列中的所有位置都进行广度优先搜索,类似二叉树层序遍历的流程)能够记录当前分钟数。
同时,考虑到不一定所有的橘子都会腐烂,需要额外使用一个计数变量表示当前还剩余的新鲜橘子数量,一旦新鲜橘子变为 0,直接退出广度优先搜索即可。最终若计数变量为 0,返回分钟数;若计数变量不为 0,表示不是所有橘子都会腐烂,返回 -1。
class Solution {
private:
int directions[4][2] = {{1, 0}, {-1, 0}, {0, 1}, {0, -1}};
public:
int orangesRotting(vector<vector<int>>& grid) {
int m = grid.size(), n = grid[0].size();
queue<pair<int, int>> q;
int count = 0;
for(int i = 0; i < m; i++)
for(int j = 0; j < n; j++){
if(grid[i][j] == 2)
q.push({i, j});
if(grid[i][j] == 1)
count++;
}
int minute = 0;
while(count != 0 && !q.empty()){
minute++;
int size = q.size();
for(int i = 0; i < size; i++){
auto p = q.front();
q.pop();
for(auto direction: directions){
int x = p.first + direction[0], y = p.second + direction[1];
if(x >= 0 && x < m && y >= 0 && y < n && grid[x][y] == 1){
grid[x][y] = 2;
q.push({x, y});
count--;
}
}
}
}
return count == 0 ? minute : -1;
}
};