📜  子矩阵求和查询(1)

📅  最后修改于: 2023-12-03 15:25:02.048000             🧑  作者: Mango

子矩阵求和查询

子矩阵求和查询是指对于给定的一个矩阵,查询其中给定子矩阵中所有元素的和。这道问题在二维数组处理中非常常见,其简化版即为一维数组中给定区间的求和问题。

解题思路

考虑到使用前缀和可以较为方便地实现一维数组中区间的求和,我们可以类比一维数组,将二维矩阵拆分为多个一维矩阵,并对其每行和每列分别计算前缀和。

接下来假设我们已经获得了每行和每列的前缀和数组 row_sumcol_sum,以及需要查询的子矩阵左上角和右下角的坐标 left_upright_down。那么此时我们可以根据前缀和数组很容易地得到该子矩阵的和,具体来说,对于二维矩阵中任意一个点 (i,j),它在子矩阵中的贡献为:

$$ f(i,j) = \begin{cases} row_sum_i - row_sum_{left_up.x - 1} &0 \leq i < left_up.x\\ col_sum_j - col_sum_{left_up.y - 1} &0 \leq j < left_up.y\\ row_sum_{right_down.x} - row_sum_i &right_down.x < i < row_sum_size()\\ col_sum_{right_down.y} - col_sum_j &right_down.y < j < col_sum_size() \end{cases} $$

其中第一行和第二行分别表示该点在子矩阵的上半部分和左半部分,第三行和第四行分别表示该点在子矩阵的下半部分和右半部分。对于一个查询询问,我们需要对所有子矩阵内的点进行统计,可以在 O(row\_sum\_size() * col\_sum\_size()) 的时间复杂度内完成。

代码实现

下面我们将 C++ 代码片段给出,其中前缀和数组的计算需要先遍历整个二维矩阵,总时间复杂度为 O(row\_sum\_size() * col\_sum\_size())

class SubMatrixSum {
public:
    SubMatrixSum(vector<vector<int>>& matrix) {
        // 计算每行和每列的前缀和
        int n = matrix.size(), m = matrix[0].size();
        row_sum = vector<vector<int>>(n, vector<int>(m+1, 0));
        col_sum = vector<vector<int>>(m, vector<int>(n+1, 0));
        for (int i = 0; i < n; ++i) {
            for (int j = 0; j < m; ++j) {
                row_sum[i][j+1] = row_sum[i][j] + matrix[i][j];
                col_sum[j][i+1] = col_sum[j][i] + matrix[i][j];
            }
        }
    }
    
    int sumRegion(int row1, int col1, int row2, int col2) {
        int sum = 0;
        for (int i = row1; i <= row2; ++i) {
            sum += row_sum[i][col2+1] - row_sum[i][col1];
        }
        for (int j = col1; j <= col2; ++j) {
            sum += col_sum[j][row2+1] - col_sum[j][row1];
        }
        return sum;
    }
    
private:
    vector<vector<int>> row_sum, col_sum;
};
总结

子矩阵求和查询是二维数组中的一个经典问题,它与一维数组中的区间求和问题类似,可以通过拆分为多个一维数组并对其进行前缀和的计算来解决。对于一个查询询问,我们需要对所有子矩阵内的点进行遍历,总时间复杂度为 O(row\_sum\_size() * col\_sum\_size())