Problem: 2258. 逃离火灾
思路
看到官方第二种方法使用了三次bfs,其实可以在第二次bfs时直接记录
“能走到当前格子,且在此格子能待的最长时间”,当然由于bfs是多路并进,我们要找多路中到达安全屋的“能走到当前格子,且在此格子能待的最长时间的”最大值,优化思路是在该值为负的时候直接放弃这一路
解题方法
使用restTime记录
“能走到当前格子,且在此格子能待的最长时间”,方法是用fireTime即第一遍bfs计算到的烧到此格的时间减去到达该格子的时间,然后与前一个格子的restTime做min计算,最后在安全屋将每一路得到的restTime值做max计算,从而得到能在原点待的最长值,ans初值赋为-1,当无解时返回的ans就是-1
复杂度
- 时间复杂度:
O(nm)
- 空间复杂度:
O(nm)
Code
class Solution {
public final int INF = 1000000000;
public final int[][] dirs = {{0, 1}, {0, -1}, {1, 0}, {-1, 0}};
class Position {
public int x;
public int y;
Position(int _x, int _y) {
x = _x;
y = _y;
}
}
public int maximumMinutes(int[][] grid) {
int n = grid.length, m = grid[0].length;
//每个格子被火烧到的时间
int fireTimes[][] = new int[n][m];
// bfs检索每个格子被火烧到的时间
fireSpread(grid, fireTimes);
int ans = arriveTime(grid, fireTimes);
if(ans > -1 && fireTimes[n - 1][m - 1] == INF) return INF;
return ans < -1 || ans >= INF ? -1 : ans;
// return ans;
}
void fireSpread(int[][] grid, int[][] fireTimes) {
Queue<Position> firePositions = new ArrayDeque<Position>();
int n = grid.length, m = grid[0].length;
// 找到火的初始位置
for (int i = 0; i < n; i++)
for (int j = 0; j < m; j++)
if (grid[i][j] == 1) firePositions.offer(new Position(i, j));
for (int i = 0; i < n; i++) {
Arrays.fill(fireTimes[i], INF);
}
for (Position position : firePositions) {
fireTimes[position.x][position.y] = 0;
}
boolean burt[][] = new boolean[n][m];
// bfs找到火每次到达到达某个格子需要的时间
while (!firePositions.isEmpty()) {
// Position position = new Position();
Position position = firePositions.poll();
for (int dir[] : dirs) {
int nextX = position.x + dir[0];
int nextY = position.y + dir[1];
if (nextX < 0 || nextX >= n || nextY < 0 || nextY >= m || burt[nextX][nextY] || grid[nextX][nextY] == 2)
continue;
else {
fireTimes[nextX][nextY] = fireTimes[position.x][position.y] + 1;
burt[nextX][nextY] = true;
firePositions.offer(new Position(nextX, nextY));
}
}
}
}
int arriveTime(int[][] grid, int[][] fireTimes) {
int n = grid.length, m = grid[0].length;
boolean visited[][] = new boolean[n][m];
Queue<int[]> queue = new ArrayDeque<int[]>();
queue.offer(new int[]{0, 0, 0});
visited[0][0] = true;
int ans = -1;
while (!queue.isEmpty()) {
int[] position = queue.poll();
int restTime = fireTimes[position[0]][position[1]] - position[2] - 1; // 能走到此处且此处能待的剩余时间
if(restTime < 0) { // 优化,如果中途就被火烧了就直接结束这一路
visited[position[0]][position[1]] = true;
continue;
}
for (int[] dir : dirs) {
// 下坐标
int nextX = position[0] + dir[0];
int nextY = position[1] + dir[1];
// 到达下结点的时间
int time = position[2] + 1;
//越界,墙,已拜访
if (nextX < 0 || nextX >= n || nextY < 0 || nextY >= m || visited[nextX][nextY] || grid[nextX][nextY] == 2)
continue;
else {
//抵达安全屋
if (nextX == n - 1 && nextY == m - 1) {
restTime = Math.min(restTime, fireTimes[nextX][nextY] - time);
ans = Math.max(ans, restTime);
continue;
}
//火未达此处
//记录路径最小值
if (fireTimes[nextX][nextY] > time) {
// 火烧身前能走到此处且此处能待的剩余时间
restTime = Math.min(restTime, fireTimes[nextX][nextY] - time - 1);
queue.offer(new int[]{nextX, nextY, time, restTime});
visited[nextX][nextY] = true;
}
}
}
}
return ans;
}
}