【数据结构-二维前缀最小值】力扣3148. 矩阵中的最大得分

给你一个由 正整数 组成、大小为 m x n 的矩阵 grid。你可以从矩阵中的任一单元格移动到另一个位于正下方或正右侧的任意单元格(不必相邻)。从值为 c1 的单元格移动到值为 c2 的单元格的得分为 c2 - c1 。

你可以从 任一 单元格开始,并且必须至少移动一次。

返回你能得到的 最大 总得分。

示例 1:

输入:grid = [[9,5,7,3],[8,9,6,1],[6,7,14,3],[2,5,3,1]]

输出:9

解释:从单元格 (0, 1) 开始,并执行以下移动:

  • 从单元格 (0, 1) 移动到 (2, 1),得分为 7 - 5 = 2 。
  • 从单元格 (2, 1) 移动到 (2, 2),得分为 14 - 7 = 7 。
    总得分为 2 + 7 = 9 。

示例 2:

输入:grid = [[4,3,2],[3,2,1]]

输出:-1

解释:从单元格 (0, 0) 开始,执行一次移动:从 (0, 0) 到 (0, 1) 。得分为 3 - 4 = -1 。

动态规划+前缀最小值

cpp 复制代码
class Solution {
public:
    int maxScore(vector<vector<int>>& grid) {
        int m = grid.size(), n = grid[0].size();
        vector<vector<int>> f(m+1, vector<int>(n+1, INT_MAX));
        int ans = INT_MIN;
        for(int i = 0; i < m; i++){
            for(int j = 0; j < n; j++){
                int mn = min(f[i+1][j],f[i][j+1]);
                ans = max(ans, grid[i][j] - mn);
                f[i+1][j+1] = min(mn, grid[i][j]);
            }
        }
        return ans;
    }
};

时间复杂度 :O(mn),其中 m 和 n 分别为 mat 的行数和列数。
空间复杂度:O(mn)。

这道题首先要化简问题,示例1中:

从单元格 (0, 1) 移动到 (2, 1),得分为 7 - 5 = 2 。

从单元格 (2, 1) 移动到 (2, 2),得分为 14 - 7 = 7 。

7-5 + 14 - 7 = 9,9是总得分。可以发现,计算总得分只需要终点14和起点5,通过14-5就可以得到。那么就说明,这道题就有了个初始的思路,遍历网格每个元素,然后通过某个容器储存这个元素为右下角(终点)的子矩阵中,子矩阵左上角(也就是起点)的最小值是多少。

所以在每次遍历元素的时候,我们定义一个局部变量mn,int mn = min(f[i+1][j],f[i][j+1]);,f[i+1][j+1]的含义是左上角为(0,0),右下角为(i,j)的子矩阵的最小值。因为在我们遍历到某个元素时,他的路径要么从左边来,也就是f[i+1][j],要么从上面来,也就是f[i][j+1],也就是说,当grid[i][j]为终点的时候,要在以(0,0)为左上角,(i,j)为右下角的矩阵中(不包括(i,j))找到最小的那个元素值mn。然后将grid[i][j] - mn的值来更新ans。最后更新f[i+1][j+1],如果grid[i][j]比mn还小的话,那么就其更新为grid[i][j]。

优化:维护列

cpp 复制代码
class Solution {
public:
    int maxScore(vector<vector<int>>& grid) {
        int m = grid.size(), n = grid[0].size();
        vector<int> col_min(n, INT_MAX);
        int ans = INT_MIN;
        for(int i = 0; i < m; i++){
            int pre_min = INT_MAX;
            for(int j = 0; j < n; j++){
                ans = max(ans, grid[i][j] - min(pre_min, col_min[j]));
                col_min[j] = min(col_min[j], grid[i][j]);
                pre_min = min(pre_min, col_min[j]);
            }
        }
        return ans;
    }
};

时间复杂度 :O(mn),其中 m 和 n 分别为 mat 的行数和列数。
空间复杂度:O(n)。

整体思路和第一种方法一样,维护列运用到了滚动数组的思路。我们只需要维护列,也就是矩阵每一列的最小值,然后在遍历的过程中,不断更新列的最小值。

首先我们在第一种方法中,在遍历某一格元素时,我们需要的信息是左上角是(0,0),右下角是(i,j)的矩阵的最小元素值。如果用列来表示我们需要的信息,也就是从0到j-1列的最小值,然后第j列的从0到i-1行的最小值,这两个最小值我们进行比较,更小的那个就是我们需要的信息。在代码中表示min(pre_min, col_min[j])

所以说在遍历每一行的时候,我们都要重置pre_min为INT_MAX,pre_min的作用就是找到从0到j-1列的最小值,col_min的作用就是储存第j列的从0到i-1行的最小值。

cpp 复制代码
 ans = max(ans, grid[i][j] - min(pre_min, col_min[j]));

我们可以更新ans的值。

计算完ans后,就要更新pre_min的值为接下来的运算做准备,更新pre_min又需要更新col_min。在更新之前,col_min[j]记载的是第j列的从0到上一行的最小值,更新后就是第j列从0到这一行的最小值。然后知道col_min后,由于pre_min的作用就是找到从0到j-1列的最小值,所以我们pre要更新到第j列,才能供j+1列使用。所以我们只需要比较第j列的最小值有没有比0到j-1列的最小值更小,如果更小的话,那么就更新pre_min的值为该值。

最后遍历完所有元素后,返回ans。

相关推荐
L_09076 小时前
【C++】高阶数据结构 -- 红黑树
数据结构·c++
划破黑暗的第一缕曙光10 小时前
[数据结构]:5.二叉树链式结构的实现1
数据结构
青桔柠薯片10 小时前
数据结构:单向链表,顺序栈和链式栈
数据结构·链表
XiaoFan01210 小时前
将有向工作流图转为结构树的实现
java·数据结构·决策树
睡一觉就好了。10 小时前
快速排序——霍尔排序,前后指针排序,非递归排序
数据结构·算法·排序算法
齐落山大勇11 小时前
数据结构——单链表
数据结构
皮皮哎哟11 小时前
深入浅出双向链表与Linux内核链表 附数组链表核心区别解析
c语言·数据结构·内核链表·双向链表·循环链表·数组和链表的区别
wWYy.12 小时前
指针与引用区别
数据结构
VT.馒头12 小时前
【力扣】2625. 扁平化嵌套数组
前端·javascript·算法·leetcode·职场和发展·typescript
历程里程碑12 小时前
Linux 17 程序地址空间
linux·运维·服务器·开发语言·数据结构·笔记·排序算法