
大家好,我是小卡皮巴拉
文章目录
目录
[兄弟们共勉 !!!](#兄弟们共勉 !!!)
每篇前言
博客主页:小卡皮巴拉
咱的口号:🌹小比特,大梦想🌹
作者请求:由于博主水平有限,难免会有错误和不准之处,我也非常渴望知道这些错误,恳请大佬们批评斧正。
力扣题目:矩阵区域和
原题链接:1314. 矩阵区域和 - 力扣(LeetCode)
题目描述
给你一个 m x n
的矩阵 mat
和一个整数 k
,请你返回一个矩阵 answer
,其中每个 answer[i][j]
是所有满足下述条件的元素 mat[r][c]
的和:
i - k <= r <= i + k,
j - k <= c <= j + k
且(r, c)
在矩阵内。
示例 1:
输入:mat = [[1,2,3],[4,5,6],[7,8,9]], k = 1
输出:[[12,21,16],[27,45,33],[24,39,28]]
示例 2:
输入:mat = [[1,2,3],[4,5,6],[7,8,9]], k = 2
输出:[[45,45,45],[45,45,45],[45,45,45]]
解题思路
问题理解
本题要求对于给定的 m x n
矩阵 mat
和整数 k
,计算一个新的矩阵 answer
,其中 answer[i][j]
是满足 i - k <= r <= i + k
且 j - k <= c <= j + k
的所有 mat[r][c]
元素的和,同时要保证 (r, c)
在矩阵内。
算法选择
采用二维前缀和的算法。通过预处理一个前缀和矩阵,可以在 O(1) 的时间复杂度内计算出任意子矩阵的和,从而将整体的时间复杂度降低到 O(mn)。
具体思路
-
预处理前缀和矩阵:
-
创建一个大小为
(m + 1) x (n + 1)
的二维数组dp
作为前缀和矩阵,多一行一列是为了方便处理边界情况。 -
使用双重循环遍历矩阵
mat
,对于每个位置(i, j)
,计算dp[i][j]
的值,其计算公式为dp[i][j] = dp[i - 1][j] + dp[i][j - 1] - dp[i - 1][j - 1] + mat[i - 1][j - 1]
。这里减去dp[i - 1][j - 1]
是为了避免重复计算左上角的元素。
-
-
计算结果矩阵:
-
创建一个大小为
m x n
的二维数组ret
作为结果矩阵。 -
再次使用双重循环遍历矩阵
mat
的每个元素(i, j)
。 -
对于每个元素
(i, j)
,计算其对应的子矩阵的左上角坐标(x1, y1)
和右下角坐标(x2, y2)
,使用max
和min
函数确保坐标不越界。 -
利用前缀和矩阵
dp
计算子矩阵的和,公式为ret[i][j] = dp[x2][y2] - dp[x1 - 1][y2] - dp[x2][y1 - 1] + dp[x1 - 1][y1 - 1]
。
-
-
返回结果 :
- 遍历完矩阵
mat
后,ret
矩阵中存储的就是最终结果,返回ret
。
- 遍历完矩阵
解题要点
-
前缀和矩阵的计算:正确理解和使用二维前缀和的状态转移方程,避免重复计算元素。
-
边界处理 :在计算子矩阵的左上角和右下角坐标时,使用
max
和min
函数确保坐标不越界。 -
子矩阵和的计算:利用前缀和矩阵的性质,通过四个前缀和的加减运算在 O(1) 的时间复杂度内计算出子矩阵的和。
完整代码(C++)
cpp
class Solution {
public:
vector<vector<int>> matrixBlockSum(vector<vector<int>>& mat, int k)
{
// 获取矩阵 mat 的行数 m 和列数 n
int m = mat.size(), n = mat[0].size();
// 1. 预处理一个前缀和矩阵
// dp 矩阵用于存储前缀和,大小为 (m + 1) x (n + 1),多一行一列是为了方便处理边界情况
vector<vector<int>> dp(m + 1, vector<int>(n + 1));
// 双重循环遍历矩阵,计算前缀和
for(int i = 1; i <= m; i++)
for(int j = 1; j <= n; j++)
// 状态转移方程:当前位置的前缀和等于上方位置的前缀和加上左方位置的前缀和
// 减去左上方位置的前缀和(避免重复计算),再加上当前矩阵元素的值
dp[i][j] = dp[i - 1][j] + dp[i][j - 1] - dp[i - 1][j - 1] + mat[i - 1][j - 1];
// 2. 使用前缀和矩阵计算结果矩阵
// ret 矩阵用于存储最终结果,大小为 m x n
vector<vector<int>> ret(m, vector<int>(n));
// 双重循环遍历矩阵 mat 的每个元素
for(int i = 0; i < m; i++)
{
for(int j = 0; j < n; j++)
{
// 计算当前元素 (i, j) 对应的子矩阵的左上角坐标 (x1, y1)
// 使用 max 函数确保坐标不越界,加 1 是因为 dp 矩阵多了一行一列
int x1 = max(0, i - k) + 1, y1 = max(0, j - k) + 1;
// 计算当前元素 (i, j) 对应的子矩阵的右下角坐标 (x2, y2)
// 使用 min 函数确保坐标不越界,加 1 是因为 dp 矩阵多了一行一列
int x2 = min(m - 1, i + k) + 1, y2 = min(n - 1, j + k) + 1;
// 利用前缀和矩阵计算子矩阵的和
// 公式为:右下角前缀和 - 左上角上方前缀和 - 左上角左方前缀和 + 左上角左上方前缀和
ret[i][j] = dp[x2][y2] - dp[x1 - 1][y2] - dp[x2][y1 - 1] + dp[x1 - 1][y1 - 1];
}
}
// 返回结果矩阵
return ret;
}
};
兄弟们共勉 !!!
码字不易,求个三连
抱拳了兄弟们!
