leetcode
leetcode 2251 ~ 2300
在网格图中访问一个格子的最少时间

在网格图中访问一个格子的最少时间

难度:

标签:

题目描述

You are given a m x n matrix grid consisting of non-negative integers where grid[row][col] represents the minimum time required to be able to visit the cell (row, col), which means you can visit the cell (row, col) only when the time you visit it is greater than or equal to grid[row][col].

You are standing in the top-left cell of the matrix in the 0th second, and you must move to any adjacent cell in the four directions: up, down, left, and right. Each move you make takes 1 second.

Return the minimum time required in which you can visit the bottom-right cell of the matrix. If you cannot visit the bottom-right cell, then return -1.

 

Example 1:

Input: grid = [[0,1,3,2],[5,1,2,5],[4,3,8,6]]
Output: 7
Explanation: One of the paths that we can take is the following:
- at t = 0, we are on the cell (0,0).
- at t = 1, we move to the cell (0,1). It is possible because grid[0][1] <= 1.
- at t = 2, we move to the cell (1,1). It is possible because grid[1][1] <= 2.
- at t = 3, we move to the cell (1,2). It is possible because grid[1][2] <= 3.
- at t = 4, we move to the cell (1,1). It is possible because grid[1][1] <= 4.
- at t = 5, we move to the cell (1,2). It is possible because grid[1][2] <= 5.
- at t = 6, we move to the cell (1,3). It is possible because grid[1][3] <= 6.
- at t = 7, we move to the cell (2,3). It is possible because grid[2][3] <= 7.
The final time is 7. It can be shown that it is the minimum time possible.

Example 2:

Input: grid = [[0,2,4],[3,2,1],[1,0,4]]
Output: -1
Explanation: There is no path from the top left to the bottom-right cell.

 

Constraints:

  • m == grid.length
  • n == grid[i].length
  • 2 <= m, n <= 1000
  • 4 <= m * n <= 105
  • 0 <= grid[i][j] <= 105
  • grid[0][0] == 0

 

代码结果

运行时间: 728 ms, 内存: 29.1 MB


/*
 * 思路:
 * 1. 使用广度优先搜索(BFS)来寻找从左上角到右下角的最早时间。
 * 2. 使用一个优先级队列来存储和处理当前能够到达的格子,优先访问时间最小的格子。
 * 3. 对每个格子,检查其四个方向的相邻格子,如果可以在当前时间+1时刻访问,加入队列继续搜索。
 * 4. 记录访问过的格子,避免重复访问。
 */
import java.util.*;
import java.util.stream.*;

public class Solution {
    public int earliestArrivalTime(int[][] grid) {
        int m = grid.length, n = grid[0].length;
        int[][] directions = {{0, 1}, {1, 0}, {0, -1}, {-1, 0}};
        PriorityQueue<int[]> pq = new PriorityQueue<>(Comparator.comparingInt(a -> a[2]));
        boolean[][] visited = new boolean[m][n];
        pq.offer(new int[]{0, 0, 0});

        while (!pq.isEmpty()) {
            int[] curr = pq.poll();
            int x = curr[0], y = curr[1], time = curr[2];
            if (x == m - 1 && y == n - 1) return time;
            if (visited[x][y]) continue;
            visited[x][y] = true;

            Arrays.stream(directions)
                .map(dir -> new int[]{x + dir[0], y + dir[1]})
                .filter(pos -> pos[0] >= 0 && pos[0] < m && pos[1] >= 0 && pos[1] < n && !visited[pos[0]][pos[1]])
                .forEach(pos -> pq.offer(new int[]{pos[0], pos[1], Math.max(time + 1, grid[pos[0]][pos[1]])}));
        }
        return -1;
    }
}

解释

方法:

这道题目利用的是优先队列(最小堆)的方式来找出最早到达矩阵右下角的路径。算法开始时,将起始点 (0, 0) 的时间为 0 加入堆中。然后,依次从堆中取出当前时间最小的点,更新它的邻居节点。如果移动到邻居节点的时间小于该邻居节点在 grid 中指定的最早可访问时间,那么我们需要等待,直到满足条件。我们不断地从堆中取点和更新点,直到到达矩阵的右下角。如果在某个点的时间超过了我们在此之前访问过该点的时间,就无需再处理它。这是因为我们已经找到了一种更早到达该点的方式。如果我们在堆为空时还没有到达终点,说明无法到达终点,返回 -1。

时间复杂度:

O((m*n) * log(m*n))

空间复杂度:

O(m*n)

代码细节讲解

🦆
在算法中使用优先队列(最小堆)是基于什么考虑?为什么这是处理此问题的合适数据结构?
优先队列(最小堆)能够保证总是从队列中取出当前具有最小计时的节点,这对于本题中寻找最短路径至关重要。使用最小堆可以快速地访问并更新到达每个点的最早可能时间,确保算法的效率。这种结构适合处理问题,因为它可以高效地管理和更新待处理节点的优先级,适用于图中的最短路径搜索,如Dijkstra算法。
🦆
优先队列中的元素是按照什么标准进行排序的?为什么选择这种排序方式?
在本题的优先队列中,元素按照从网格的起点(左上角)到当前点的时间(距离)进行排序,具有最小时间的点优先处理。选择这种排序方式可以确保每次都处理当前可达的最早的点,这有助于快速找到路径,并在满足条件时立即停止进一步搜索,从而提高效率。
🦆
如果在堆为空时仍未到达终点返回 -1,这种情况通常在什么样的场景下发生?如何从输入数据的角度预测这种情况?
如果堆为空时仍未到达终点,通常表明从起点到终点之间存在不可逾越的障碍或条件限制,使得路径不可到达。例如,如果起始点周围的单元格的时间值过高,导致无法从起点开始。从输入数据的角度预测这种情况,可以检查起始点周围的邻居节点的时间值,如果这些值都设置得过高,或者网格配置使得所有通路都被封锁,那么可能预见到返回 -1 的情况。

相关问题