青少儿编程课堂头像

蓝桥杯·全国青少年信息素养大赛·电子学会软件编程考级:二维前缀和(子矩阵快速求和)解析

在 蓝桥杯软件赛(C/C++·Python 程序设计)、全国青少年信息素养大赛算法应用主题赛,以及中国电子学会软件编程(C/C++、Python)等级考试的真题里,矩阵、网格统计类题目几乎年年出现:给你一张成绩单矩阵,反复询问「某一块矩形区域的总分是多少」。如果每次询问都暴力求和,数据一大就超时;而 二维前缀和 能用一次预处理,把任意子矩阵的求和压到 O(1)。本文用一道原创题讲透它的原理、代码(C++ / Python 双版)、七个易错点和进阶变形。

锚点速览(备赛正当时):第十八届蓝桥杯软件赛报名 2026-10-08 10:00—2026-12-18 14:00,省赛 2027-04-10(C/C++·Python 程序设计 9:00–13:00),总决赛 2027-06-12(dasai.lanqiao.cn);中国电子学会 2026 年 12 月软件编程(C/C++ 一级至十级、Python 一级至六级)等级考试报名 9/23–12/2 16:00、统考 12-12(C/C++ 8:30–10:30、Python 14:00–15:00);全国青少年信息素养大赛 2026—2027 赛季算法应用主题赛。

一、原创题目:校园科技节方阵得分统计

【题目描述】
校园科技节上,n 行 m 列的展位方阵中,每个展位有一个整数得分(可能为负数,表示扣分项)。赛后组委会要回答 q 次询问:每次给出子矩阵左上角 (x1, y1) 和右下角 (x2, y2)(坐标均为 1 起始、闭区间),求该矩形内所有展位得分的总和。

【输入格式】
第一行 n, m, q;接下来 n 行每行 m 个整数(展位得分);接下来 q 行每行四个整数 x1, y1, x2, y2。

【输出格式】
对每个询问输出一行,为该子矩阵得分之和。

【数据范围】
1 ≤ n, m ≤ 1000,1 ≤ q ≤ 100000,得分 ∈ [−1000, 1000]。

【样例】
矩阵:

1  2  3
4  5  6
7  8  9
  • 询问 2 2 3 3(右下 2×2:5,6 / 8,9)→ 输出 28
  • 询问 1 1 3 3(整个矩阵)→ 输出 45

暴力做法对每个询问 O(n·m),总复杂度 O(q·n·m) ≈ 10⁹,必然超时——这正是二维前缀和要解决的问题。

二、核心考点拆解

  1. 前缀和的本质是「容斥」:一维前缀和是「前面一段的和」;二维前缀和则是「左上角到 (i,j) 这个矩形的和」,查询时用四个角做加减抵消,得到任意子矩阵的和。
  2. 建表 O(n·m):先把每个 (i,j) 的「以 (1,1) 为左上、以 (i,j) 为右下的矩形和」算好,存进 s[i][j]。
  3. 查询 O(1):任意子矩阵和 = s[x2][y2] − s[x1−1][y2] − s[x2][y1−1] + s[x1−1][y1−1]。
  4. 1 起始下标偏移技巧:矩阵用 1 起始、前缀和数组多开一行一列(s[0][*] = s[*][0] = 0),这样 x1=1 时 s[0][*] 自然为 0,查询不用特判越界——这是写对的关键。

三、二维前缀和公式

建表递推(经典「上 + 左 − 左上的左上」):

s[i][j] = a[i][j] + s[i-1][j] + s[i][j-1] - s[i-1][j-1]

子矩阵求和(「右下 − 上多算 − 左多算 + 重复减掉的一次」):

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

四个项 「两减一加」 的顺序和符号必须严格对齐,写错一个符号结果就会变成 0 或乱值。

四、解法与代码(C++ / Python 双版)

4.1 Python 实现

def build_prefix(a, n, m):
    # a 为 0 起始二维列表;返回 1 起始前缀和 s(s[0][*]=s[*][0]=0)
    s = [[0] * (m + 1) for _ in range(n + 1)]
    for i in range(1, n + 1):
        for j in range(1, m + 1):
            s[i][j] = a[i-1][j-1] + s[i-1][j] + s[i][j-1] - s[i-1][j-1]
    return s

def rect_sum(s, x1, y1, x2, y2):
    # 1 起始闭区间 [x1,x2] × [y1,y2]
    return s[x2][y2] - s[x1-1][y2] - s[x2][y1-1] + s[x1-1][y1-1]

# 样例验证
a = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
s = build_prefix(a, 3, 3)
print(rect_sum(s, 2, 2, 3, 3))  # 28
print(rect_sum(s, 1, 1, 3, 3))  # 45

4.2 C++ 实现

#include <bits/stdc++.h>
using namespace std;
using ll = long long;

// 构建二维前缀和(1 起始)
vector<vector<ll>> buildPrefix(const vector<vector<int>>& a, int n, int m) {
    vector<vector<ll>> s(n + 1, vector<ll>(m + 1, 0));
    for (int i = 1; i <= n; ++i)
        for (int j = 1; j <= m; ++j)
            s[i][j] = (ll)a[i-1][j-1] + s[i-1][j] + s[i][j-1] - s[i-1][j-1];
    return s;
}

// O(1) 子矩阵求和
ll rectSum(const vector<vector<ll>>& s, int x1, int y1, int x2, int y2) {
    return s[x2][y2] - s[x1-1][y2] - s[x2][y1-1] + s[x1-1][y1-1];
}

int main() {
    vector<vector<int>> a = {{1,2,3},{4,5,6},{7,8,9}};
    int n = 3, m = 3;
    auto s = buildPrefix(a, n, m);
    cout << rectSum(s, 2, 2, 3, 3) << "\n";  // 28
    cout << rectSum(s, 1, 1, 3, 3) << "\n";  // 45
    return 0;
}

4.3 时间 / 空间复杂度

  • 时间:建表 O(n·m),每次询问 O(1),总 O(n·m + q)。q=10⁵、n·m=10⁶ 轻松通过。
  • 空间:前缀和数组 O(n·m)。注意 C++ 用 long long:满分可达 1000×1000×1000 = 10⁹,远超过 int 上限(约 2.1×10⁹ 看似够,但负分累加会越界),务必开 long long;Python 自动大整数无此忧。

五、七个易错点(提分必看)

  1. 下标偏移:统一用 1 起始 + 多开一行一列,避免 x1−1 当 x1=1 时越界;用 0 起始写查询极易在边界 WA。
  2. 容斥符号:四项是「右下 − 上 − 左 + 左上」,少加或多减都会算错,建议背熟再默写。
  3. 建表顺序:i、j 必须从小到大递推,否则依赖的前缀还没算出来。
  4. 坐标体系:题目给的可能是 0 起始,进场先统一 +1 转成 1 起始;混用直接错。
  5. 矩形合法性:若题目不保证 x1≤x2,进场用 swap/取 min,max 防御,否则下标倒挂会越界。
  6. 初始值误导:若题目要求「最大子矩阵和」,初值必须用 -INF 而非 0(矩阵可能全负),否则答案永远 ≥0。
  7. 整数溢出:C++ 必须用 long long 存前缀和与答案;只把矩阵本身存 int 即可。

六、进阶:最大子矩阵和(蓝桥杯省赛经典变形)

把题目升级为:求整个矩阵中元素和最大的子矩阵(可为任意位置、任意大小)。这正是「二维前缀和 + 一维最大连续子段和(Kadane)」的经典组合。

思路:枚举上边界 top 与下边界 bottom(O(n²)),把这两行之间的每一列「压」成一维数组(列和),然后在该一维数组上跑 Kadane 求最大连续子段和,全局取最大。复杂度 O(n²·m)。

Python 实现:

def max_submatrix(a, n, m):
    best = -10**18
    for top in range(n):
        col = [0] * m
        for bottom in range(top, n):
            for j in range(m):
                col[j] += a[bottom][j]          # 把 [top,bottom] 行压成一维
            cur = 0; mx = -10**18
            for v in col:                        # Kadane 一维最大子段和
                cur = max(v, cur + v)
                if cur > mx: mx = cur
            if mx > best: best = mx
    return best

# 样例(全正矩阵)最大即整体 45
print(max_submatrix([[1,2,3],[4,5,6],[7,8,9]], 3, 3))  # 45

C++ 实现:

ll maxSubmatrix(const vector<vector<int>>& a, int n, int m) {
    ll best = -1e18;
    for (int top = 0; top < n; ++top) {
        vector<ll> col(m, 0);
        for (int bottom = top; bottom < n; ++bottom) {
            for (int j = 0; j < m; ++j) col[j] += a[bottom][j];
            ll cur = 0, mx = -1e18;
            for (ll v : col) { cur = max(v, cur + v); if (cur > mx) mx = cur; }
            best = max(best, mx);
        }
    }
    return best;
}

进阶延伸:① 二维差分——与二维前缀和「互逆」,用于「矩形区域整体加一个值」,同样 O(1) 修改、最后 O(n·m) 还原;② 高维前缀和——三维体积统计;③ 二维哈希——快速判断两个子矩阵是否完全相同;④ 前缀和优化 DP——很多网格 DP 的转移可用前缀和把 O(n) 降到 O(1)。

七、小结与互动

二维前缀和是「以空间换时间」思想的典范:一次 O(n·m) 预处理,换取任意子矩阵 O(1) 查询,是蓝桥杯网格题、电子学会考级数组章节、信息素养大赛算法赛项的必备工具。记住口诀「建表:上+左−左上;查询:右下−上−左+左上」,再避开上面的七个坑,这一类题就能稳定拿分。

互动:你在刷题时遇到过哪些二维前缀和相关的「坑」或好题?欢迎在评论区留言,下一篇我们可以聊聊它的孪生兄弟——二维差分与矩形区间修改。


📚 免费少儿编程资料(夸克网盘领取)

以下资料来自夸克网盘分享,点击链接可直接保存;若需在 App 内打开,也可复制下方明文链接:

  1. 全国青少年信息素养大赛复赛集训题目Python&C++.docx
    https://pan.quark.cn/s/93995d3cb150
  2. 2024信息素养-智能算法应用挑战赛-复赛初中组题目7月7日.pdf
    https://pan.quark.cn/s/da97b5dbf75d
  3. Python背记手册.pdf
    https://pan.quark.cn/s/7568ae9ca92b
  4. Python课程
    https://pan.quark.cn/s/a94bf02d00c6
  5. 2024信息素养大赛图形化复赛集训题答案3-9
    https://pan.quark.cn/s/6ccab7ec3cbc
  6. 2025年03月份电子学会考级真题
    https://pan.quark.cn/s/4403c4228912
  7. 2025全国青少年信息素养大赛赛项说明
    https://pan.quark.cn/s/d9d0df4a9f29
  8. 青少儿信息素养大赛编程资料
    https://pan.quark.cn/s/4ab6bd83be8a

资料持续更新,关注获取最新分享。

转载自 CSDN-专业IT技术社区

原文链接:https://blog.csdn.net/IT_Scratch/article/details/170755141

文章来源转载

评论

评论列表

微信小程序
QQ小程序

关于作者

点赞数:0
关注数:0
粉丝:0
文章:0
关注标签:0
加入于:--