前缀和——矩阵区域和

一.题目描述

1314. 矩阵区域和 - 力扣(LeetCode)

二.题目解析

题目乍一看就是求个矩阵,仔细一看这矩阵砸球啊?题目叽里咕噜说什么呢?下面用图来展示一下,题目的要求:

answerij位置的元素就是以mat对应位置为中心,然后k为半径的矩阵的和。

题目所求的矩阵, 本质就是需要求一段矩阵的和,所以快速求矩阵的和我们就可以使用二维前缀和来解决问题。

三.算法原理

二维前缀和

1.预处理二维前缀和数组

我们首先回顾一下二维前缀和数组每个位置表示的是什么:dpij表示的是原数组从(1,1)~(i,j)这个矩阵的和。

2.使用前缀和数组

我们使用二维前缀和数组就可以求出原数组中某一段区间的和

接下里我们要将其融入到我们的answer中,answer求得也是一个区间的和,但是我们只知道中心下标,为了求这个区间的和,我们得知道这个区间的左上和右下的下标,依次来使用前缀和数组:

但是需要注意的是,我们求这连个点的下标时,有可能会导致越界。就如示例一种answer的(0,0)位置的值,它的左上下标已经越界了,所以我们要对其进行处理。

如果左上的下标为负数了,其应该变为0;如果右下的下标大于n-1/m-1,就应该变为n-1/m-1。

这里有一个简单的方法来修正下标:

x1 = max(0,i-k),y1 = max(0,j-k);x2 = min(n-1,i+k),y2 = min(m-1,j+k)

最后将该作坐标带入上面的公式即可。

3.细节处理

我们这里的mat数组是从0,0位置开始的。而我们在预处理dp数组时,为了避免边界情况(计算后的下标小于0),下标是从1,1开始的。所以我们在预处理时加最后的d时,其实对应的是mat数组的mati-1j-1

还有就是使用dp数组时,answer返回数组的下标是从0,0开始的,而dp数组从1,1开始才是有效数据。我们计算下标是按照answer来计算的,而我们下标最后应用的数组却是dp数组。所以要给计算来的下标加上1.

x1 = max(0,i-k) + 1,y1 = max(0,j-k)+ 1;x2 = min(n-1,i+k)+ 1,y2 = min(m-1,j+k)+ 1.

时间复杂度:预处理二维数组时需要遍历一遍矩阵,后面使用二维数组都是常数级的时间复杂度,所以时间复杂度为O(MN)

空间复杂度:我们额外开了一个(m+1)*(n+1)大的空间来存储前缀和数组,所以空间复杂度为O(MN)

四.代码实现

复制代码
// C++

class Solution 
{
public:
    vector<vector<int>> matrixBlockSum(vector<vector<int>>& mat, int k) 
    {
        int m = mat.size();
        int n = mat[0].size();
        vector<vector<int>> answer(m,vector<int>(n));

        // 初始化dp数组
        vector<vector<int>> dp(m+1,vector<int>(n+1));
        for(int i=1; i<m+1; ++i)
            for(int j=1; j<n+1; ++j)
                dp[i][j] = dp[i][j-1] + dp[i-1][j] - dp[i-1][j-1] + mat[i-1][j-1];

        // 使用dp数组
        for(int i=0; i<m; ++i)
            for(int j=0; j<n; ++j)
            {
                int x1 = max(0,i-k) + 1, y1 = max(0,j-k) + 1;
                int x2 = min(m-1,i+k) + 1, y2 = min(n-1,j+k) + 1;

                answer[i][j] = dp[x2][y2] - dp[x2][y1-1] - dp[x1-1][y2] + dp[x1-1][y1-1];
            }
        return answer;
    }
};

# python

class Solution:
    def matrixBlockSum(self, mat: List[List[int]], k: int) -> List[List[int]]:
        m,n = len(mat),len(mat[0])
        answer = [[0]*n for _ in range(m)]

        # 预处理二维前缀和数组
        dp = [[0]*(n+1)for _ in range(m+1)]
        for i in range(1,m+1):
            for j in range(1,n+1):
                dp[i][j] = dp[i][j-1] + dp[i-1][j]+mat[i-1][j-1]-dp[i-1][j-1]
        
        # 使用前缀和数组
        for i in range(0,m):
            for j in range(0,n):
                x1,y1 = max(0,i-k) + 1,max(0,j-k) + 1
                x2,y2 = min(m-1,i+k) + 1,min(n-1,j+k) + 1
                answer[i][j] = dp[x2][y2] - dp[x2][y1-1] - dp[x1-1][y2] + dp[x1-1][y1-1]
        return answer
相关推荐
Tisfy1 小时前
LeetCode 2333.最小差值平方和:排序 + 数学计算(不用二分)
数学·算法·leetcode·题解·贪心·排序
aqiu1111111 小时前
【C++算法打怪专栏】LeetCode 217. 存在重复元素(解法总结)
c++·算法·leetcode
yi0112 小时前
DAY28: LeetCode 111. 二叉树的最小深度
人工智能·笔记·python·算法·leetcode·二叉树·递归
wuyk5553 小时前
27.LeetCode 2265. 统计值等于子树平均值的节点数 —— 后序遍历实战
stm32·单片机·嵌入式硬件·算法
xiangyun613 小时前
C++ list 模拟实现(二):const迭代器与反向迭代器,模板的黑魔法
开发语言·数据库·c++·算法·stl·list
xxwl5853 小时前
三、区域赛算法模版第三章---字符串相关算法
笔记·学习·算法
信奥卷王3 小时前
[GESP202509 六级] 划分字符串(含视频)
数据结构·算法
酒醉的胡铁3 小时前
相似度搜索的常见算法
算法·ai
局部有解3 小时前
Leetcode Hot100 - 最长连续序列
算法·leetcode·职场和发展
行者全栈架构师4 小时前
给开源 Rust 工具加一个新检查项,TRAE 帮我从想法跑到构建产物:全链路实战
前端·算法·架构