2617. 网格图中最少访问的格子数 【维护每行,每列的最小堆】

28 阅读2分钟

2617. 网格图中最少访问的格子数

class Solution:
    def minimumVisitedCells(self, grid: List[List[int]]) -> int:
        # 维护 每行, 每列 的最小堆   记录能到达的最大位置
        # 对于 行最小堆 row,  如果 堆顶位置的 最远可到达的列号  小于拟到的列号 j, 弹出。可到达 j, 更新堆顶 
        # 对于 列最小堆 col,  如果 堆顶位置的 最远可到达的行号 小于拟到的行号 i, 弹出。可到达 i, 更新堆顶 

        m, n = len(grid), len(grid[0])
        dist = [[-1] * n for _ in range(m)]  # 达到 i, j 所需的最小格子数
        dist[0][0] = 1 # 格子数
        row = [[] for _ in range(m)]  # 每行的 最小堆  [[[cnt, 列编号], ...], [[cnt, 列编号], ...],[[cnt, 列编号], ...]... ]
        col = [[] for _ in range(n)]  # 每列的 最小堆  [cnt, 行编号]

        def update(x, y):
            return y if x == -1 or y < x else x 

        for i in range(m):
            for j in range(n):  # 到达
                #  维护 第 i 行的最小堆  [cnt, 列编号] row[i][0]表示 最小堆堆顶元素
                while row[i] and row[i][0][1] + grid[i][row[i][0][1]] < j:  # 列不可达
                    heapq.heappop(row[i])
                if row[i]:  # 第 i 行中, 可达第 j 列的 最小格子数 的位置
                    dist[i][j] = update(dist[i][j], dist[i][row[i][0][1]] + 1)

                # 维护 第 j 列的最小堆 [cnt, 行编号]
                while col[j] and col[j][0][1] + grid[col[j][0][1]][j] < i:
                    heapq.heappop(col[j])
                if col[j]: # 第 j 列中, 可达第 i 行的 最小格子数 的位置
                    dist[i][j] = update(dist[i][j], dist[col[j][0][1]][j] + 1)

                if dist[i][j] != -1:
                    heapq.heappush(row[i], (dist[i][j], j))
                    heapq.heappush(col[j], (dist[i][j], i))
        return dist[m - 1][n - 1]                   

image.png

class Solution {
public:
    int minimumVisitedCells(vector<vector<int>>& grid) {
        int m = grid.size(), n = grid[0].size();
        vector<vector<int>> dist(m, vector<int>(n, -1)); // 到达 格子 i, j 所需经过的最小格子数
        dist[0][0] = 1;  // 原点算一个格子
        // 小顶堆
        vector<priority_queue<pair<int, int>, vector<pair<int, int>>, greater<>>> row(m), col(n);  // [cnt, 列编号]  [cnt, 行编号]

        auto update = [](int& x, int y){
            if (x == -1 || y < x){
                x = y;
            }
        };

        for (int i = 0; i < m; ++i){
            for (int j = 0; j < n; ++j){
                // 去掉 列不可达 j 的 行小堆顶点
                while (!row[i].empty() && row[i].top().second + grid[i][row[i].top().second] < j){
                    row[i].pop();
                }
                if (!row[i].empty()){
                    update(dist[i][j], dist[i][row[i].top().second] + 1);
                }

                // 去掉 行不可达 i 的 列小堆顶点  [cnt, 行编号]
                while (!col[j].empty() && col[j].top().second + grid[col[j].top().second][j] < i){
                    col[j].pop();// 不可达  弹出
                }
                if (!col[j].empty()){
                    update(dist[i][j], dist[col[j].top().second][j] + 1);
                }
                if (dist[i][j] != -1){
                    row[i].emplace(dist[i][j], j);
                    col[j].emplace(dist[i][j], i);
                }

            }
        }
        return dist[m - 1][n - 1];        
    }
};