【CSP考点回顾】二维前缀和数组(代码实现)

1. 初始化扩展的二维前缀和数组

创建一个大小为 (rows + 1) x (cols + 1) 的二维前缀和数组,其中 rowscols 分别是原始数组的行数和列数。然后,我们按以下方式填充这个数组:

void initPrefixSum(vector<vector<int>>& prefixSum, const vector<vector<int>>& matrix) {
    int rows = matrix.size();
    int cols = matrix[0].size();
    // 初始化扩展了的前缀和数组
    prefixSum.resize(rows + 1, vector<int>(cols + 1, 0));

    for (int i = 1; i <= rows; ++i) {
        for (int j = 1; j <= cols; ++j) {
            prefixSum[i][j] = matrix[i - 1][j - 1]
                            + prefixSum[i - 1][j]
                            + prefixSum[i][j - 1]
                            - prefixSum[i - 1][j - 1];
        }
    }
}

2. 查询扩展的二维前缀和数组

由于我们的前缀和数组是扩展过的,因此查询时的坐标也需要相应地调整。例如,如果我们要查询原始数组中从 (x1, y1)(x2, y2) 的子矩阵的和,我们可以使用以下代码进行查询:

int queryPrefixSum(const vector<vector<int>>& prefixSum, int x1, int y1, int x2, int y2) {
    // 调整索引,因为前缀和数组是扩展了的
    int sum = prefixSum[x2 + 1][y2 + 1]
            - prefixSum[x1][y2 + 1]
            - prefixSum[x2 + 1][y1]
            + prefixSum[x1][y1];
    return sum;
}

注意,在查询时,我们需要对原始数组的索引进行加1处理,因为我们的前缀和数组的起始索引是从1开始的,而非0。这种方式简化了边界检查的需求,因为所有的从原点到(x, y)的查询都可以直接使用上面的公式,不再需要特别处理边界情况。

3.二维前缀和数组还原原始数组

前缀和数组是通过累加原始数组中的元素构建的,因此还原原始数组实质上是一个逆累加(或逆差分)的过程。下面是一个实现这一过程的C++函数示例。前缀和数组大小为 (rows + 1) x (cols + 1),并且我们希望从这个扩展的前缀和数组还原出原始的大小为 rows x cols 的数组:

vector<vector<int>> restoreOriginalArray(const vector<vector<int>>& prefixSum) {
    int rows = prefixSum.size() - 1;  // 原始数组的行数
    int cols = prefixSum[0].size() - 1;  // 原始数组的列数
    vector<vector<int>> originalArray(rows, vector<int>(cols, 0));

    for (int i = 1; i <= rows; ++i) {
        for (int j = 1; j <= cols; ++j) {
            // 还原原始数组的值
            originalArray[i - 1][j - 1] = prefixSum[i][j]
                                        - prefixSum[i - 1][j]
                                        - prefixSum[i][j - 1]
                                        + prefixSum[i - 1][j - 1];
        }
    }
    
    return originalArray;
}

在这个函数中,我们遍历扩展后的前缀和数组,使用类似于查询前缀和的反向过程来计算原始数组的每个元素。每个元素的值是由当前位置的前缀和减去其上方和左侧的前缀和,再加上其左上角(即前一个)的前缀和得到的。这正好是构建前缀和时使用的逆过程。这个函数返回的 originalArray 就是还原后的原始二维数组。

4.完整代码(附测试样例)

#include <iostream>
#include <vector>

using namespace std;

// 初始化扩展的二维前缀和数组
void initPrefixSum(vector<vector<int>>& prefixSum, const vector<vector<int>>& matrix) {
    int rows = matrix.size();
    int cols = matrix[0].size();
    prefixSum.resize(rows + 1, vector<int>(cols + 1, 0));

    for (int i = 1; i <= rows; ++i) {
        for (int j = 1; j <= cols; ++j) {
            prefixSum[i][j] = matrix[i - 1][j - 1]
                + prefixSum[i - 1][j]
                + prefixSum[i][j - 1]
                - prefixSum[i - 1][j - 1];
        }
    }
}

// 查询扩展的二维前缀和数组中子矩阵的和
int queryPrefixSum(const vector<vector<int>>& prefixSum, int x1, int y1, int x2, int y2) {
    int sum = prefixSum[x2 + 1][y2 + 1]
        - prefixSum[x1][y2 + 1]
        - prefixSum[x2 + 1][y1]
        + prefixSum[x1][y1];
    return sum;
}

// 从扩展的二维前缀和数组还原原始数组
vector<vector<int>> restoreOriginalArray(const vector<vector<int>>& prefixSum) {
    int rows = prefixSum.size() - 1;
    int cols = prefixSum[0].size() - 1;
    vector<vector<int>> originalArray(rows, vector<int>(cols, 0));

    for (int i = 1; i <= rows; ++i) {
        for (int j = 1; j <= cols; ++j) {
            originalArray[i - 1][j - 1] = prefixSum[i][j]
                - prefixSum[i - 1][j]
                - prefixSum[i][j - 1]
                + prefixSum[i - 1][j - 1];
        }
    }

    return originalArray;
}

int main() {
    vector<vector<int>> matrix = {
        {1, 2, 3},
        {4, 5, 6},
        {7, 8, 9}
    };
    vector<vector<int>> prefixSum;
    initPrefixSum(prefixSum, matrix);

    cout << "Prefix Sum Array:" << endl;
    for (const auto& row : prefixSum) {
        for (int val : row) {
            cout << val << " ";
        }
        cout << endl;
    }

    int sum = queryPrefixSum(prefixSum, 1, 1, 2, 2); // 查询从(1, 1)到(2, 2)的子矩阵的和
    cout << "Queried sum: " << sum << endl;

    vector<vector<int>> restoredArray = restoreOriginalArray(prefixSum);
    cout << "Restored Original Array:" << endl;
    for (const auto& row : restoredArray) {
        for (int val : row) {
            cout << val << " ";
        }
        cout << endl;
    }

    return 0;
}
  • 17
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值