关于小羊仔的刷题记录
- 题目描述
- 解答(动态规划)
- 另解(2D卷积法)
- 总结
题目描述
难度mediun
给你一个 m * n 的矩阵 mat 和一个整数 K ,请你返回一个矩阵 answer ,其中每个 answer[i][j] 是所有满足下述条件的元素 mat[r][c] 的和:
i - K <= r <= i + K, j - K <= c <= j + K
(r, c) 在矩阵内。
解答(动态规划)
class Solution:def matrixBlockSum(self, mat: List[List[int]], K: int) -> List[List[int]]:m = len(mat)n = len(mat[0])dp = [[mat[0][0] for _ in range(n)] for _ in range(m)]out = [[0 for _ in range(n)] for _ in range(m)]for i in range(1, n):dp[0][i] = dp[0][i-1] + mat[0][i] for j in range(1, m):dp[j][0] = dp[j-1][0] + mat[j][0]for i in range(1, m):for j in range(1, n):dp[i][j] = dp[i-1][j] + dp[i][j-1] + mat[i][j] - dp[i-1][j-1] for i in range(m):for j in range(n):x1 = max(0, i-K)y1 = max(0, j-K)x2 = min(m-1, i+K)y2 = min(n-1, j+K)if x1 == 0 and y1 == 0:out[i][j] = dp[x2][y2]elif x1 == 0 and y1 != 0:out[i][j] = dp[x2][y2] - dp[x2][y1-1]elif x1!=0 and y1==0:out[i][j] = dp[x2][y2] -dp[x1-1][y2]else:out[i][j] = dp[x2][y2] - dp[x1-1][y2] - dp[x2][y1-1] + dp[x1-1][y1-1]return out
矩阵区域和问题,运用动态规划思想,将求解每一块区域和分解为更容易解决的小问题。
我们用dp[i][j]记录原矩阵从[0][0]到[i][j]的这个矩形区域的和,通过使用dp中的元素进行计算可以得到原矩阵中任意矩形区域的和。
原矩阵中的点加减K得到的矩形框可能会超出原矩阵区域,所以要对矩形框设限,
x1 = max(0, i-K)
y1 = max(0, j-K)
x2 = min(m-1, i+K)
y2 = min(n-1, j+K)
此即为矩形框左上角点和右下角点的横纵坐标。
需要求和的原矩阵的矩形区域可以根据左上角点的位置分为四类:
a)左上角点横纵坐标均为0,此时out[i][j] = dp[x2][y2]
b)左上角点横坐标为0纵坐标不为0,此时out[i][j] = dp[x2][y2] - dp[x2][y1-1]
c)左上角点横坐标不为0纵坐标为0,此时out[i][j] = dp[x2][y2] -dp[x1-1][y2]
d)左上角点横纵坐标均不为0,此时out[i][j] = dp[x2][y2] - dp[x1-1][y2] - dp[x2][y1-1] + dp[x1-1][y1-1]
(此横纵坐标与正常数学坐标系横纵相反,这么表达是为了方便理解,请领会精神)
遍历原矩阵即可得到结果矩阵
另解(2D卷积法)
class Solution:def matrixBlockSum(self, mat: List[List[int]], K: int) -> List[List[int]]:import numpy as npfrom scipy import signalnp_mat = np.array(mat)np_filter = np.ones((2 * K + 1, 2 * K + 1))res = signal.convolve2d(np_mat, np_filter, mode='same')return res.astype(int).tolist()
对CNN比较熟悉的同学可能看到题目以后很快会想到这实际上在进行中卷积计算。卷积核大小为(2K+1)*(2K+1),所有元素均为1。通过调库进行卷积计算,可以很快得到答案。
总结
思路指向还是很明显,但能够正确写出代码还是需要一定的勇气和细心。尤其是分情况讨论时,矩阵数据如何对应。画图理解会容易很多。
作者在调试过程中也是遇到了一些坑,比如说开始矩阵赋值的时候用的是[[0]*n]*m,后来发现这种赋值方法产生的二维数组相当于对一维数组进行复制,只要改变其中一个一维数组其他数组也会跟着改变。应该用[[0]*n for _ in range(m)]这种赋值方法比较好。
我因此调试的时候多花了很多时间,后来查到后才想起来我好像以前也犯过这个错误,是我太菜,菜鸡本?,呜呜呜。