LeetCode每日一题:2258.逃离火灾

108 阅读2分钟

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;
    }
}