蓝桥杯C++基础算法-子矩阵的和

这段代码实现了一个二维前缀和的计算和查询功能。它的核心思想是通过预先计算一个二维前缀和数组 s,使得每次查询某个子矩阵的和时,可以在常数时间内完成。以下是代码的详细思路解析:


1. 问题背景

假设有一个二维数组 a,大小为 n × m,我们需要多次查询某个子矩阵的和。直接计算子矩阵和的方法是遍历子矩阵内的所有元素并累加,但这会导致每次查询的时间复杂度为 O(n × m),效率较低。为了优化查询效率,可以使用二维前缀和技术。


2. 二维前缀和的概念

二维前缀和数组 s 是一个辅助数组,其中 s[i][j] 表示从 (1, 1)(i, j) 的子矩阵内所有元素的累加和,即:

cpp复制

s[i][j] = sum(a[x][y]) for all 1 ≤ x ≤ i and 1 ≤ y ≤ j

通过二维前缀和数组,可以快速计算任意子矩阵 (x1, y1)(x2, y2) 的和:

cpp复制

sum(x1, y1, x2, y2) = s[x2][y2] - s[x1 - 1][y2] - s[x2][y1 - 1] + s[x1 - 1][y1 - 1]

这里的关键是利用了二维前缀和的性质,通过加减四个部分的和来得到目标子矩阵的和。


3. 代码逻辑解析

(1) 输入部分

cpp复制

cin >> n >> m >> q;
for (int i = 1; i <= n; i++)
    for (int j = 1; j <= m; j++)
        cin >> a[i][j];
  • 用户输入矩阵的行数 n、列数 m 和查询次数 q

  • 接着输入矩阵 a 的每个元素。矩阵 a 的索引从 (1, 1) 开始,方便后续计算。

(2) 二维前缀和数组的计算

cpp复制

for (int i = 1; i <= n; i++)
    for (int j = 1; j <= m; j++)
        s[i][j] = s[i - 1][j] + s[i][j - 1] - s[i - 1][j - 1] + a[i][j];
  • 使用双重循环计算二维前缀和数组 s

    • s[i][j] 的值等于:

      • s[i - 1][j]:上一行的前缀和。

      • s[i][j - 1]:左边列的前缀和。

      • - s[i - 1][j - 1]:减去重复计算的部分(左上角的前缀和)。

      • + a[i][j]:加上当前元素的值。

  • 这样,s[i][j] 就存储了从 (1, 1)(i, j) 的子矩阵内所有元素的累加和。

(3) 查询部分

cpp复制

while (q--)
{
    int x1, y1, x2, y2;
    cin >> x1 >> y1 >> x2 >> y2;
    cout << s[x2][y2] - s[x1 - 1][y2] - s[x2][y1 - 1] + s[x1 - 1][y1 - 1] << endl;
}
  • 用户输入查询的子矩阵范围 (x1, y1)(x2, y2)

  • 利用二维前缀和数组快速计算子矩阵的和:

    • s[x2][y2]:整个大矩阵的前缀和。

    • - s[x1 - 1][y2]:减去上方多余部分的前缀和。

    • - s[x2][y1 - 1]:减去左侧多余部分的前缀和。

    • + s[x1 - 1][y1 - 1]:加上被重复减去的部分(左上角的前缀和)。

  • 输出查询结果。


4. 代码效率分析

  • 时间复杂度

    • 计算二维前缀和数组的时间复杂度为 O(n × m)

    • 每次查询的时间复杂度为 O(1)

    • 总时间复杂度为 O(n × m + q),其中 n × m 是矩阵大小,q 是查询次数。

  • 空间复杂度

    • 使用了一个额外的二维前缀和数组 s,空间复杂度为 O(n × m)


5. 示例运行

输入:

plaintext复制

3 3 2
1 2 3
4 5 6
7 8 9
1 1 2 2
2 2 3 3
运行过程:
  1. 输入矩阵大小 n = 3m = 3 和查询次数 q = 2

  2. 输入矩阵 a

    复制

    1 2 3
    4 5 6
    7 8 9
  3. 计算二维前缀和数组 s

    复制

    s[0][0] = 0
    s[1][1] = 1
    s[1][2] = 3
    s[1][3] = 6
    s[2][1] = 5
    s[2][2] = 12
    s[2][3] = 21
    s[3][1] = 12
    s[3][2] = 27
    s[3][3] = 45
  4. 处理查询:

    • 查询 (1, 1)(2, 2) 的和:

      cpp复制

      s[2][2] - s[0][2] - s[2][0] + s[0][0] = 12 - 0 - 0 + 0 = 12
    • 查询 (2, 2)(3, 3) 的和:

      cpp复制

      s[3][3] - s[1][3] - s[3][1] + s[1][1] = 45 - 6 - 12 + 1 = 28
输出:

plaintext复制

12
28

6. 总结

这段代码的核心思路是通过二维前缀和技术,将子矩阵和查询的时间复杂度从 O(n × m) 优化到 O(1)。通过预先计算一个二维前缀和数组 s,可以快速完成多次子矩阵和查询,适用于需要频繁查询子矩阵和的场景。

#include<bits/stdc++.h>
using namespace std;
const int N = 1010;

int n, m, q;
int a[N][N], s[N][N];

int main()
{
	cin>>n>>m>>q;
	for(int i = 1; i <= n; i ++)
		for(int j = 1; j <= m; j ++)
			cin>>a[i][j];
	for(int i = 1; i <= n; i ++)
		for(int j = 1; j <= m; j ++)
			s[i][j] += s[i - 1][j] + s[i][j - 1] - s[i - 1][j - 1] + a[i][j];
	while(q--)
	{
		int x1, y1, x2, y2;
		cin>>x1>>y1>>x2>>y2;
		cout<<s[x2][y2] - s[x1 - 1][y2] - s[x2][y1 - 1] + s[x1 - 1][y1 - 1] << endl;
	}
	return 0;
} 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值