元素和小于等于 k 的子矩阵的数目

标签: 数组 矩阵 前缀和

难度: Medium

给你一个下标从 0 开始的整数矩阵 grid 和一个整数 k

返回包含 grid 左上角元素、元素和小于或等于 k子矩阵的数目。

示例 1:

输入:grid = [[7,6,3],[6,6,1]], k = 18
输出:4
解释:如上图所示,只有 4 个子矩阵满足:包含 grid 的左上角元素,并且元素和小于或等于 18 。

示例 2:

输入:grid = [[7,2,9],[1,5,0],[2,6,6]], k = 20
输出:6
解释:如上图所示,只有 6 个子矩阵满足:包含 grid 的左上角元素,并且元素和小于或等于 20 。

提示:

  • m == grid.length
  • n == grid[i].length
  • 1 <= n, m <= 1000
  • 0 <= grid[i][j] <= 1000
  • 1 <= k <= 109

Submission

运行时间: 87 ms

内存: 50.7 MB

class Solution:
    def countSubmatrices(self, grid: List[List[int]], k: int) -> int:#按行转移
        m,n=len(grid),len(grid[0])
        rowPrev=[0]#算行列前缀和on2
        sm=0#sum(grid[0])
        res=0#终止于每行的子矩阵数
        for i in range(m):
            if n<1:break
            sm+=sum(grid[i][:n])
            while sm>k:
                sm-=sum(grid[j][n-1] for j in range(i+1))
                n-=1
            res+=n
        return res

Explain

题解的思路是通过逐行累加行的和,并同时在每一行中逐列考虑减少列的数量以保证子矩阵的和小于等于k。具体来说,首先初始化行前缀和为0,然后逐行计算当前行到第一行的矩阵和。如果这个和超过k,就减少列数,直到和小于等于k或者列数减到0。每减少一次列数,就从当前的总和中减去所有行在该列的元素和。这样,每一行结束时,n的值就是从第一列开始到第n列的子矩阵的数量,我们将这个数量累加到结果中。

时间复杂度: O(m * n^2)

空间复杂度: O(1)

class Solution:
    def countSubmatrices(self, grid: List[List[int]], k: int) -> int: # 按行处理子矩阵
        m, n = len(grid), len(grid[0])
        rowPrev = [0] # 初始化行前缀和
        sm = 0 # 初始化第一行的和
        res = 0 # 初始化结果
        for i in range(m): # 遍历每一行
            if n < 1: break # 如果列数为0,则停止
            sm += sum(grid[i][:n]) # 计算当前行到第一行的和
            while sm > k: # 当子矩阵的和大于k时,减少列数
                sm -= sum(grid[j][n-1] for j in range(i+1)) # 减去每行在当前最后一列的元素和
                n -= 1 # 减少列数
            res += n # 将当前行的子矩阵数量加到结果中
        return res

Explore

选择以行为基础进行累加和的计算主要是因为这种方式可以有效地利用二维数组的行连续存储特性,从而简化计算过程。在大多数编程环境中,二维数组是按行优先存储的,这意味着行内的数据在内存中是连续的。因此,按行累加可以更高效地访问内存,减少缓存未命中的可能性,提高算法的执行效率。此外,以行为单位处理子矩阵可以较容易地实现前缀和的动态更新,便于处理和计算各种范围内的子矩阵和。

是的,该算法在减少列数来调整子矩阵的和小于等于k时,有可能错过某些满足条件的子矩阵配置。算法通过逐步减少列数来确保当前考虑的子矩阵的和不超过k,但这种方法可能没有考虑到通过移除非连续列(非最右侧列)或通过在某些行中移除列而在其他行中保留更多列的可能配置。这种策略本质上是贪心的,并且仅考虑了从第一列开始到当前列的连续子矩阵,遗漏了其它潜在的、不连续的或从中间某列开始的子矩阵配置。

这种方法不能保证找到所有可能的子矩阵配置。如前面所述,算法主要是在当前行的基础上连续减少列数,以确保子矩阵的和小于等于k。这种连续的、基于当前行的列减少策略可能忽略了一些非连续的列配置或者某些特定行列组合,这些组合可能同样满足总和小于等于k的条件。因此,虽然这种方法在特定情况下效率较高,但它不是一个全局最优解,不能覆盖所有可能的满足条件的子矩阵配置。