【数据结构-二维前缀最小值】力扣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]);,fi+1j+1的含义是左上角为(0,0),右下角为(i,j)的子矩阵的最小值。因为在我们遍历到某个元素时,他的路径要么从左边来,也就是f[i+1][j],要么从上面来,也就是f[i][j+1],也就是说,当gridij为终点的时候,要在以(0,0)为左上角,(i,j)为右下角的矩阵中(不包括(i,j))找到最小的那个元素值mn。然后将gridij - mn的值来更新ans。最后更新fi+1j+1,如果gridij比mn还小的话,那么就其更新为gridij。

优化:维护列

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_minj记载的是第j列的从0到上一行的最小值,更新后就是第j列从0到这一行的最小值。然后知道col_min后,由于pre_min的作用就是找到从0到j-1列的最小值,所以我们pre要更新到第j列,才能供j+1列使用。所以我们只需要比较第j列的最小值有没有比0到j-1列的最小值更小,如果更小的话,那么就更新pre_min的值为该值。

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

相关推荐
BK贝壳6 小时前
bool-hybrid-array 落盘我只认 .bha:用 Create_BHA / Ask_BHA 做布尔数组序列化
数据结构·python·numpy·1024程序员节
?abc!10 小时前
(数组)581. 最短无序连续子数组
数据结构·算法·leetcode
hold?fish:palm12 小时前
40 二叉树的直径
c++·leetcode
guo_wen_qiang13 小时前
二叉树的层序遍历-使用双端队列Deque
算法·leetcode·1024程序员节
爱读书的小胖14 小时前
LeetCode 55 跳跃游戏 C 语言解题分析
c语言·数据结构·算法·leetcode·1024程序员节
舰长11514 小时前
将百万数据插入到 Redis,有哪些实现方案?
数据结构·redis·开源
Navigator_Z15 小时前
LeetCode //C - 1300. Sum of Mutated Array Closest to Target
c语言·算法·leetcode
若亦_15 小时前
数据结构>>优先级队列(堆)
java·数据结构
凯瑟琳.奥古斯特16 小时前
力扣1016高效解法:剪枝优化技巧
开发语言·c++·算法·leetcode·职场和发展
bksczm16 小时前
Redis数据结构之hash
数据结构·redis·哈希算法·1024程序员节