跳到主要内容
极客日志极客日志面向AI+效率的开发者社区
首页博客GitHub 精选镜像工具UI配色美学隐私政策关于联系
搜索内容 / 工具 / 仓库 / 镜像...⌘K搜索
注册
博客列表
C++算法

算法基础:前缀和技巧与区间求和优化

综述由AI生成前缀和是一种通过预处理数据将区间查询复杂度从线性降至常数级别的算法技巧。本文详细讲解了一维和二维前缀和的核心原理、递推公式及代码实现,涵盖最大子段和与激光炸弹等经典应用场景。通过空间换时间的策略,前缀和能有效优化静态数组的区间求和问题,是算法竞赛与工程实践中不可或缺的基础工具。

月亮邮递员发布于 2026/3/26更新于 2026/6/215 浏览
算法基础:前缀和技巧与区间求和优化

算法基础:前缀和技巧与区间求优化

在算法设计与性能优化中,前缀和(Prefix Sum)是一种简单却极具威力的技巧。它通过预处理数据,将原本需要线性遍历的区间求和问题转化为常数时间的查询,实现了典型的'空间换时间'策略。无论是处理一维数组的区间累加,还是解决二维矩阵的子区域统计,掌握前缀和都能显著提升代码效率。

本文将深入探讨一维和二维前缀和的核心原理、实现细节及典型应用场景,帮助你理解这一基础算法如何在竞赛与工程实践中改变问题的复杂度格局。

一维前缀和

核心原理

对于静态数组的多次区间求和查询,暴力模拟每次遍历的时间复杂度高达 O(n*q),极易超时。前缀和通过预先计算每个位置之前的累加和,将单次查询降至 O(1)。

定义前缀和数组 f,其中 f[i] 表示原数组 a 从下标 1 到 i 的元素之和。递推公式如下:

f[i] = f[i - 1] + a[i]

当需要查询区间 [l, r] 的和时,利用容斥原理,结果即为:

sum(l, r) = f[r] - f[l - 1]

注意:通常为了处理边界情况方便,数组下标从 1 开始,且 f[0] 初始化为 0。

代码实现

#include <iostream>
using namespace std;

const int N = 1e5 + 10;
typedef long long LL;

int n, q;
LL a[N];
LL f[N]; // 前缀和数组

int main() {
    cin >> n >> q;
    // 读入原始数据
    for (int i = 1; i <= n; i++) {
        cin >> a[i];
    }

    // 预处理前缀和
    for (int i = 1; i <= n; i++) {
        f[i] = f[i - 1] + a[i];
    }

    
     (q--) {
         l, r;
        cin >> l >> r;
        cout << f[r] - f[l - ] << endl;
    }
     ;
}
// 处理 q 次询问
while
int
1
return
0

扩展应用:最大子段和

在经典的最大子段和问题中,我们同样可以利用前缀和来优化。如果以位置 i 结尾的最大子段和已知,那么该值等于 f[i] 减去 i 之前所有前缀和中的最小值。

思路是维护一个变量记录当前遇到的最小前缀和 prevmin,遍历过程中不断更新答案 ret。

#include <iostream>
#include <algorithm>
using namespace std;

typedef long long LL;
const int N = 2e5 + 10;

int n;
LL f[N]; // 前缀和数组

int main() {
    cin >> n;
    for (int i = 1; i <= n; i++) {
        LL x;
        cin >> x;
        f[i] = f[i - 1] + x;
    }

    LL ret = -1e20; // 初始化为极小值
    LL prevmin = 0; // 前缀和最小值,初始为 f[0]=0

    for (int i = 1; i <= n; i++) {
        ret = max(ret, f[i] - prevmin);
        prevmin = min(prevmin, f[i]);
    }

    cout << ret << endl;
    return 0;
}

二维前缀和

核心原理

当问题扩展到二维矩阵时,我们需要快速计算任意子矩阵的元素和。暴力枚举子矩阵内所有元素的时间复杂度为 O(qnm),依然无法接受。

定义二维前缀和数组 f[i][j],表示从左上角 (1, 1) 到右下角 (i, j) 所围成的矩形区域内所有元素的和。

预处理公式:

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

这里减去 f[i - 1][j - 1] 是因为它在加上第一行和第二列时被重复计算了一次。

查询公式: 对于以 (x1, y1) 为左上角,(x2, y2) 为右下角的子矩阵,其和为:

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

这同样是容斥原理的应用:大矩形减去上方和左侧的矩形,再加上被多减一次的左上角重叠部分。

代码实现

#include <iostream>
using namespace std;

typedef long long LL;
const int N = 1010;

int n, m, q;
LL f[N][N];

int main() {
    cin >> n >> m >> q;

    // 预处理前缀和矩阵
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= m; j++) {
            LL x;
            cin >> x;
            f[i][j] = f[i - 1][j] + f[i][j - 1] - f[i - 1][j - 1] + x;
        }
    }

    // 处理 q 次查询
    while (q--) {
        int x1, y1, x2, y2;
        cin >> x1 >> y1 >> x2 >> y2;
        cout << f[x2][y2] - f[x1 - 1][y2] - f[x2][y1 - 1] + f[x1 - 1][y1 - 1] << endl;
    }
    return 0;
}

实战案例:激光炸弹

在实际场景中,二维前缀和常用于解决覆盖类问题。例如'激光炸弹'题目要求在一个 R*R 的正方形区域内摧毁价值最大的目标。

注意点:

  1. 题目规定若目标位于爆破正方形的边上不会被摧毁,因此实际有效范围是边长为 R 的区域内部。
  2. 坐标可能很大,但目标数量有限,或者网格大小固定。本题中网格上限为 5000。
  3. 多个目标可能位于同一坐标,需累加价值。
  4. 枚举正方形右下角 (x2, y2),左上角自然确定为 (x2 - R + 1, y2 - R + 1)。
#include <iostream>
#include <algorithm>
using namespace std;

const int N = 5010;
int n, m;
int a[N][N]; // 存储每个点的价值
int f[N][N]; // 前缀和数组

int main() {
    cin >> n >> m;
    while (n--) {
        int x, y, v;
        cin >> x >> y >> v;
        x++, y++; // 转为 1-based 索引
        a[x][y] += v; // 同一点价值累加
    }

    // 预处理前缀和
    // 注意:题目中 n 和 m 是输入的目标数,但网格大小可能更大,这里统一按 5001 处理
    int limit = 5001;
    for (int i = 1; i <= limit; i++) {
        for (int j = 1; j <= limit; j++) {
            f[i][j] = f[i - 1][j] + f[i][j - 1] - f[i - 1][j - 1] + a[i][j];
        }
    }

    int ret = 0;
    // 确保边长不超过网格实际范围
    int side = min(m, limit); 

    // 枚举所有可能的边长为 side 的正方形
    for (int x2 = side; x2 <= limit; x2++) {
        for (int y2 = side; y2 <= limit; y2++) {
            int x1 = x2 - side + 1;
            int y1 = y2 - side + 1;
            ret = max(ret, f[x2][y2] - f[x1 - 1][y2] - f[x2][y1 - 1] + f[x1 - 1][y1 - 1]);
        }
    }

    cout << ret << endl;
    return 0;
}

总结

前缀和作为一种基础而强大的算法技巧,通过预处理数据将复杂度从线性降至常数级别,彻底改变了处理区间问题的方式。从一维数组的快速求和、最大子段和优化,到二维矩阵的区域统计,前缀和以空间换时间的策略展现了极高的效率。

掌握前缀和不仅能够解决经典问题,更能为复杂场景(如动态规划、数据结构优化)提供关键思路。理解并灵活运用前缀和,是提升算法问题解决能力的重要一步。

目录

  1. 算法基础:前缀和技巧与区间求优化
  2. 一维前缀和
  3. 核心原理
  4. 代码实现
  5. 扩展应用:最大子段和
  6. 二维前缀和
  7. 核心原理
  8. 代码实现
  9. 实战案例:激光炸弹
  10. 总结
  • 💰 8折买阿里云服务器限时8折了解详情
  • Magick API 一键接入全球大模型注册送1000万token查看
  • 🤖 一键搭建Deepseek满血版了解详情
  • 一键打造专属AI 智能体了解详情
极客日志微信公众号二维码

微信扫一扫,关注极客日志

微信公众号「极客日志V2」,在微信中扫描左侧二维码关注。展示文案:极客日志V2 zeeklog

更多推荐文章

查看全部
  • 基于高云 FPGA 与 STM32 的 FMC 通信协议实现
  • RKNN 8 位量化全解析:算法差异与粒度选择实战指南
  • IntelliJ IDEA 切换 JDK 版本完整指南
  • IPv6+DDNS-GO 连接故障排查与三种替代方案对比
  • C++ TCP Socket 网络编程基础与封装实战
  • 飞算 JavaAI:基于自然语言的 Java 全栈工程生成实践
  • 开源模型 Prompt 实验报告:Mistral 与 Qwen 对比分析
  • 互联网 HR 招聘实录:张一鸣与业务负责人的用人观
  • OpenClaw 配置与 QQ 机器人接入指南
  • ToDesk ToClaw AI 科技新闻日报自动化实战
  • Agent 上下文注入原理与 Web 架构映射实战
  • Query 处理流程与数据结构解析
  • 数据结构空间复杂度详解:概念与常见计算示例
  • C++ STL list 容器详解:使用与模拟实现
  • 基于 DeepFace 和 OpenCV 的实时情绪分析器实现
  • Vue Router 进阶实战:导航守卫、嵌套路由与状态管理
  • 国内大模型公司面试经验总结与技术要点分析
  • 2025 亚洲 WEB3 商业生态创新峰会将于香港举行
  • AI 元人文:自感概念与 DOS 模型深度解析
  • Linux 内核 list_for_each_entry 链表遍历详解

相关免费在线工具

  • 加密/解密文本

    使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online

  • Gemini 图片去水印

    基于开源反向 Alpha 混合算法去除 Gemini/Nano Banana 图片水印,支持批量处理与下载。 在线工具,Gemini 图片去水印在线工具,online

  • Base64 字符串编码/解码

    将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online

  • Base64 文件转换器

    将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online

  • Markdown转HTML

    将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online

  • HTML转Markdown

    将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML转Markdown在线工具,online