leetcode 2617. 网格图中最少访问的格子数【单调栈优化dp+二分】

原题链接:2617. 网格图中最少访问的格子数

题目描述:

给你一个下标从 0 开始的 m x n 整数矩阵 grid 。你一开始的位置在 左上角 格子 (0, 0) 。

当你在格子 (i, j) 的时候,你可以移动到以下格子之一:

  • 满足 j < k <= grid[i][j] + j 的格子 (i, k) (向右移动),或者
  • 满足 i < k <= grid[i][j] + i 的格子 (k, j) (向下移动)。

请你返回到达 右下角 格子 (m - 1, n - 1) 需要经过的最少移动格子数,如果无法到达右下角格子,请你返回 -1 。

输入输出描述:

示例 1:

输入:grid = [[3,4,2,1],[4,2,3,1],[2,1,0,0],[2,4,0,0]]
输出:4
解释:上图展示了到达右下角格子经过的 4 个格子。

示例 2:

输入:grid = [[3,4,2,1],[4,2,1,1],[2,1,1,0],[3,4,1,0]]
输出:3
解释:上图展示了到达右下角格子经过的 3 个格子。

示例 3:

输入:grid = [[2,1,0],[1,0,0]]
输出:-1
解释:无法到达右下角格子。

提示:

  • m == grid.length
  • n == grid[i].length
  • 1 <= m, n <= 10^5
  • 1 <= m * n <= 10^5
  • 0 <= grid[i][j] < m * n
  • grid[m - 1][n - 1] == 0

解题思路:

这个题目我觉得出的非常好,一个非常经典的单调栈优化dp,这个题目首先每一步只能往右或者下走,很容易看出是dp,但是如果直接暴力dp,对于每个位置每次最多从n或者m个位置转移过来,那么时间复杂度就是O(n*m*(n+m)),这个时间复杂度就有点高了,我们需要考虑优化,我觉得有意思的就是这个优化,很容易可以看出需要维护的是某个区间的最小值,那么最容易想到的就是滑动窗口优化,但是这里左窗口虽然是单调减小的,但是右窗口的单调性不稳定,所以这个时候如果使用滑动窗口优化就不方便了,实际上我们考虑单调栈进行维护,我们考虑倒序枚举,在单调栈中维护一个从栈底到栈顶单调递增的序列,我们需要对每一行和每一列都维护一个单调栈,我们需要找到当前行中<=j+grid[i][j]的中的最小值,也就是需要找到从栈底到栈顶第一个列位置<=j+grid[i][j]的位置的值,我们直接在单调栈上进行二分即可,对于列做同样的处理即可。

通过上面的分析,这个问题就可以解决了,但是我写出了一个bug找了很久才找出来,那就是在单调栈维护的过程中,我们需要将行和列都更新完f[i][j]之后,才能进行去维护单调栈,因为如果我们只更新完行就去维护单调栈,但是后面更新列的时候f[i][j]又变小了,那么会导致行单调栈中的还有一些元素需要删除的没有删掉,导致后面某些位置计算到了一些错误的答案,对于只更新完列就去维护单调栈同样会出现这种错误,所以我们只有当行和列都更新完,才能去维护单调栈,这个错误我是真的看了好久才看出来,开始根本没有注意到这一点,就是一直是错的,但是我又确信我的思路应该是没有什么问题的,也就是突然之间就灵机一动意识到了这一点,不然我可能还真很难找到这个错误。

时间复杂度:O(n*m*(log(n)+log(m)))。

空间复杂度:O(n*m)。

cpp代码如下:

class Solution {
public:
    int minimumVisitedCells(vector<vector<int>>& grid) {
        int n=grid.size(),m=grid[0].size();
        vector<int>row[n],col[m];
        vector<vector<int>>f(n,vector<int>(m,1e9));
        f[n-1][m-1]=1;

        for(int i=n-1;i>=0;i--)
            for(int j=m-1;j>=0;j--)
            {
                int l=0,r=row[i].size()-1;
                while(l<r){
                    int mid=l+r>>1;
                    if(row[i][mid]<=j+grid[i][j])r=mid;
                    else l=mid+1;
                }
                if(r>=0 && row[i][r]<=j+grid[i][j]){
                    f[i][j]=min(f[i][j],f[i][row[i][r]]+1);
                }

                l=0,r=col[j].size()-1;
                while(l<r){
                    int mid=l+r>>1;
                    if(col[j][mid]<=i+grid[i][j])r=mid;
                    else l=mid+1;
                }
                if(r>=0 && col[j][r]<=i+grid[i][j]){
                    f[i][j]=min(f[i][j],f[col[j][r]][j]+1);
                }

                //上面行和列都更新完才来维护单调栈
                while(row[i].size() && f[i][row[i].back()]>=f[i][j]){
                    row[i].pop_back();
                }
                while(col[j].size() && f[col[j].back()][j]>=f[i][j]){
                    col[j].pop_back();
                }
                row[i].push_back(j);
                col[j].push_back(i);
            }
        if(f[0][0]==1e9)return -1;
        return f[0][0];
    }
};

最近更新

  1. TCP协议是安全的吗?

    2024-03-23 15:32:04       16 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2024-03-23 15:32:04       16 阅读
  3. 【Python教程】压缩PDF文件大小

    2024-03-23 15:32:04       15 阅读
  4. 通过文章id递归查询所有评论(xml)

    2024-03-23 15:32:04       18 阅读

热门阅读

  1. 力扣-字符串的最长公共前缀

    2024-03-23 15:32:04       19 阅读
  2. 力扣由浅至深 每日一题.11 加一

    2024-03-23 15:32:04       18 阅读
  3. 前端面试题整理

    2024-03-23 15:32:04       17 阅读
  4. 解决Linux报错JCE cannot authenticate the provider BC

    2024-03-23 15:32:04       16 阅读
  5. luogu P1352 没有上司的舞会 详解

    2024-03-23 15:32:04       22 阅读
  6. [Vue3] - defineProps 接收从App.vue传来的东西

    2024-03-23 15:32:04       20 阅读
  7. vuex状态管理的使用

    2024-03-23 15:32:04       18 阅读
  8. css的box-shadow详解

    2024-03-23 15:32:04       15 阅读