解法一:前缀和

先对矩阵求前缀和,有了前缀和之后就可以根据四部分的值求出任一矩阵的区域和。
注意考虑边界问题和源矩阵和前缀和矩阵之前的坐标关系。

  1. class Solution {
  2. // 行数
  3. private int row;
  4. // 列数
  5. private int col;
  6. public int[][] matrixBlockSum(int[][] mat, int K) {
  7. row = mat.length;
  8. col = mat[0].length;
  9. // preSum[i][j]表示源矩阵中以(0,0)为左上角, (i-1,j-1)为右下角的矩阵内所有元素之和
  10. // 多开空间防止边界问题
  11. int[][] preSum = getPreSum(mat);
  12. int[][] ans = new int[row][col];
  13. int[] pos;
  14. for (int i = 0; i < row; ++i) {
  15. for (int j = 0; j < col; ++j) {
  16. pos = getPosition(i, j, K);
  17. ans[i][j] = preSum[pos[2]][pos[3]] - preSum[pos[2]][pos[1]] - preSum[pos[0]][pos[3]]
  18. + preSum[pos[0]][pos[1]];
  19. }
  20. }
  21. return ans;
  22. }
  23. /**
  24. * 求解算子对应前缀和矩阵的左上角和右下角坐标
  25. * @param x 算子在源矩阵中的行坐标
  26. * @param y 算子在源矩阵中的列坐标
  27. * @param k 算子规模
  28. * @return 算子对应前缀和矩阵的左上角和右下角坐标
  29. */
  30. private int[] getPosition(int x, int y, int k) {
  31. int[] pos = new int[4];
  32. // 左上角行列坐标
  33. pos[0] = Math.max(0, x - k);
  34. pos[1] = Math.max(0, y - k);
  35. // 右下角行列坐标
  36. pos[2] = Math.min(row, x + k + 1);
  37. pos[3] = Math.min(col, y + k + 1);
  38. return pos;
  39. }
  40. /**
  41. * 求解矩阵的前缀和, ans[i][j]表示源矩阵中以(0,0)为左上角, (i-1,j-1)为右下角的矩阵内所有元素之和
  42. * @param mat 源矩阵
  43. * @return 前缀和矩阵
  44. */
  45. private int[][] getPreSum(int[][] mat) {
  46. int[][] ans = new int[row + 1][col + 1];
  47. for (int i = 1; i <= row; ++i) {
  48. for (int j = 1; j <= col; ++j) {
  49. ans[i][j] = ans[i - 1][j] + ans[i][j - 1] - ans[i - 1][j - 1] + mat[i - 1][j - 1];
  50. }
  51. }
  52. return ans;
  53. }
  54. }