算法每日双题精讲 —— 前缀和(【模板】一维前缀和,【模板】二维前缀和)

在算法竞赛与日常编程中,前缀和是一种极为实用的预处理技巧,能显著提升处理区间和问题的效率。今天,我们就来深入剖析一维前缀和与二维前缀和这两个经典模板。

一、【模板】一维前缀和

题目描述

给定一个长度为 n n n 的整数数组 a a a,我们需要完成以下两个任务:

  1. 预处理数组,得到前缀和数组。
  2. 能够快速查询数组中任意区间 [ l , r ] [l, r] [l,r] 0 ≤ l ≤ r < n 0 \leq l \leq r < n 0lr<n)内所有元素的和。

算法原理

一维前缀和的核心思想是预先计算数组中每个位置之前所有元素的总和。设前缀和数组为 s s s,其中 s [ i ] s[i] s[i] 表示数组 a a a 中前 i i i 个元素的和( i i i 1 1 1 开始),那么有递推公式:
[s[i] = s[i - 1]+a[i - 1]]
这里 s [ 0 ] = 0 s[0]=0 s[0]=0,是为了方便处理边界情况。

当我们需要查询区间 [ l , r ] [l, r] [l,r] 的和时,根据前缀和的性质,该区间的和可以通过 s [ r + 1 ] − s [ l ] s[r + 1]-s[l] s[r+1]s[l] 快速得到。这是因为 s [ r + 1 ] s[r + 1] s[r+1] 包含了前 r + 1 r + 1 r+1 个元素的和, s [ l ] s[l] s[l] 包含了前 l l l 个元素的和,两者相减就得到了区间 [ l , r ] [l, r] [l,r] 的和。

在这里插入图片描述

在这里插入图片描述

C++ 代码实现

#include <iostream>
#include <vector>

using namespace std;

// 计算一维前缀和数组
vector<int> calculatePrefixSum(const vector<int>& a) {
    int n = a.size();
    vector<int> s(n + 1, 0);
    for (int i = 1; i <= n; ++i) {
        s[i] = s[i - 1] + a[i - 1];
    }
    return s;
}

// 查询区间 [l, r] 的和
int querySum(const vector<int>& s, int l, int r) {
    return s[r + 1] - s[l];
}

int main() {
    vector<int> a = {1, 2, 3, 4, 5};
    vector<int> s = calculatePrefixSum(a);

    int l = 1, r = 3;
    cout << "The sum of the interval [" << l << ", " << r << "] is: " << querySum(s, l, r) << endl;

    return 0;
}

复杂度分析

  • 时间复杂度:计算前缀和数组的时间复杂度为 O ( n ) O(n) O(n),因为需要遍历数组一次。每次查询区间和的时间复杂度为 O ( 1 ) O(1) O(1),这使得在多次查询的场景下,一维前缀和算法具有很高的效率。
  • 空间复杂度:需要额外的长度为 n + 1 n + 1 n+1 的数组来存储前缀和,因此空间复杂度为 O ( n ) O(n) O(n)

二、【模板】二维前缀和

题目描述

给定一个 m × n m \times n m×n 的二维整数矩阵 A A A,我们要完成以下任务:

  1. 预处理矩阵,得到二维前缀和矩阵。
  2. 能够快速查询矩阵中任意子矩阵 [ ( x 1 , y 1 ) , ( x 2 , y 2 ) ] [(x_1, y_1), (x_2, y_2)] [(x1,y1),(x2,y2)] 0 ≤ x 1 ≤ x 2 < m 0 \leq x_1 \leq x_2 < m 0x1x2<m 0 ≤ y 1 ≤ y 2 < n 0 \leq y_1 \leq y_2 < n 0y1y2<n)内所有元素的和。

算法原理

对于二维前缀和,我们定义 S [ i ] [ j ] S[i][j] S[i][j] 表示矩阵 A A A 中从左上角 ( 0 , 0 ) (0, 0) (0,0) 到右下角 ( i − 1 , j − 1 ) (i - 1, j - 1) (i1,j1) 这个子矩阵内所有元素的和( i i i j j j 1 1 1 开始)。其递推公式如下:
S [ i ] [ j ] = S [ i − 1 ] [ j ] + S [ i ] [ j − 1 ] − S [ i − 1 ] [ j − 1 ] + A [ i − 1 ] [ j − 1 ] S[i][j]=S[i - 1][j]+S[i][j - 1]-S[i - 1][j - 1]+A[i - 1][j - 1] S[i][j]=S[i1][j]+S[i][j1]S[i1][j1]+A[i1][j1]
这里减去 S [ i − 1 ] [ j − 1 ] S[i - 1][j - 1] S[i1][j1] 是为了避免重复计算。

当查询子矩阵 [ ( x 1 , y 1 ) , ( x 2 , y 2 ) ] [(x_1, y_1), (x_2, y_2)] [(x1,y1),(x2,y2)] 的和时,公式为:
s u m = S [ x 2 + 1 ] [ y 2 + 1 ] − S [ x 1 ] [ y 2 + 1 ] − S [ x 2 + 1 ] [ y 1 ] + S [ x 1 ] [ y 1 ] sum = S[x_2 + 1][y_2 + 1]-S[x_1][y_2 + 1]-S[x_2 + 1][y_1]+S[x_1][y_1] sum=S[x2+1][y2+1]S[x1][y2+1]S[x2+1][y1]+S[x1][y1]

在这里插入图片描述

在这里插入图片描述

C++ 代码实现

#include <iostream>
#include <vector>

using namespace std;

// 计算二维前缀和矩阵
vector<vector<int>> calculateTwoDPrefixSum(const vector<vector<int>>& A) {
    int m = A.size();
    int n = A[0].size();
    vector<vector<int>> S(m + 1, vector<int>(n + 1, 0));
    for (int i = 1; i <= m; ++i) {
        for (int j = 1; j <= n; ++j) {
            S[i][j] = S[i - 1][j] + S[i][j - 1] - S[i - 1][j - 1] + A[i - 1][j - 1];
        }
    }
    return S;
}

// 查询子矩阵 [(x1, y1), (x2, y2)] 的和
int queryTwoDSum(const vector<vector<int>>& S, int x1, int y1, int x2, int y2) {
    return S[x2 + 1][y2 + 1] - S[x1][y2 + 1] - S[x2 + 1][y1] + S[x1][y1];
}

int main() {
    vector<vector<int>> A = {
        {1, 2, 3},
        {4, 5, 6},
        {7, 8, 9}
    };
    vector<vector<int>> S = calculateTwoDPrefixSum(A);

    int x1 = 0, y1 = 0, x2 = 1, y2 = 1;
    cout << "The sum of the sub - matrix [(" << x1 << ", " << y1 << "), (" << x2 << ", " << y2 << ")] is: "
         << queryTwoDSum(S, x1, y1, x2, y2) << endl;

    return 0;
}

复杂度分析

  • 时间复杂度:计算二维前缀和矩阵需要两层嵌套循环遍历矩阵,时间复杂度为 O ( m × n ) O(m \times n) O(m×n)。每次查询子矩阵和的时间复杂度为 O ( 1 ) O(1) O(1),这使得在多次查询子矩阵和的场景下,二维前缀和算法非常高效。
  • 空间复杂度:需要额外的 ( m + 1 ) × ( n + 1 ) (m + 1)\times(n + 1) (m+1)×(n+1) 大小的矩阵来存储二维前缀和,因此空间复杂度为 O ( m × n ) O(m \times n) O(m×n)

通过以上的讲解和代码实现,我们可以看到前缀和算法在处理区间和与子矩阵和问题时的强大威力。它通过预处理的方式,将原本可能需要 O ( n ) O(n) O(n) O ( m × n ) O(m\times n) O(m×n) 时间复杂度的查询操作优化到了 O ( 1 ) O(1) O(1),在实际应用中能显著提升程序的性能。希望大家能够熟练掌握这两个模板,并在后续的算法学习和实践中灵活运用。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/961814.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

学习数据结构(2)空间复杂度+顺序表

1.空间复杂度 &#xff08;1&#xff09;概念 空间复杂度也是一个数学表达式&#xff0c;表示一个算法在运行过程中根据算法的需要额外临时开辟的空间。 空间复杂度不是指程序占用了多少bytes的空间&#xff0c;因为常规情况每个对象大小差异不会很大&#xff0c;所以空间复杂…

MybatisX插件快速创建项目

一、安装插件 二、创建一个数据表测试 三、IDEA连接Mysql数据库 四、选择MybatiX构造器 五、配置参数 六、项目结构

基于SpringBoot的假期周边游平台的设计与实现(源码+SQL脚本+LW+部署讲解等)

专注于大学生项目实战开发,讲解,毕业答疑辅导&#xff0c;欢迎高校老师/同行前辈交流合作✌。 技术范围&#xff1a;SpringBoot、Vue、SSM、HLMT、小程序、Jsp、PHP、Nodejs、Python、爬虫、数据可视化、安卓app、大数据、物联网、机器学习等设计与开发。 主要内容&#xff1a;…

Java设计模式:结构型模式→组合模式

Java 组合模式详解 1. 定义 组合模式&#xff08;Composite Pattern&#xff09;是一种结构型设计模式&#xff0c;它允许将对象组合成树形结构以表示“部分-整体”的层次。组合模式使得客户端能够以统一的方式对待单个对象和对象集合的一致性&#xff0c;有助于处理树形结构…

FastReport.NET控件篇之富文本控件

简介 FastReport.NET 提供了 RichText 控件&#xff0c;用于在报表中显示富文本内容。富文本控件支持多种文本格式&#xff08;如字体、颜色、段落、表格、图片等&#xff09;&#xff0c;非常适合需要复杂排版和格式化的场景。 富文本控件(RichText)使用场景不多&#xff0c…

单片机基础模块学习——NE555芯片

一、NE555电路图 NE555也称555定时器,本文主要利用NE555产生方波发生电路。整个电路相当于频率可调的方波发生器。 通过调整电位器的阻值,方波的频率也随之改变。 RB3在开发板的位置如下图 测量方波信号的引脚为SIGHAL,由上面的电路图可知,NE555已经构成完整的方波发生电…

(done) MIT6.S081 2023 学习笔记 (Day6: LAB5 COW Fork)

网页&#xff1a;https://pdos.csail.mit.edu/6.S081/2023/labs/cow.html 任务1&#xff1a;Implement copy-on-write fork(hard) (完成) 现实中的问题如下&#xff1a; xv6中的fork()系统调用会将父进程的用户空间内存全部复制到子进程中。如果父进程很大&#xff0c;复制过程…

三天急速通关JavaWeb基础知识:Day 1 后端基础知识

三天急速通关JavaWeb基础知识&#xff1a;Day 1 后端基础知识 0 文章说明1 Http1.1 介绍1.2 通信过程1.3 报文 Message1.3.1 请求报文 Request Message1.3.2 响应报文 Response Message 2 XML2.1 介绍2.2 利用Java解析XML 3 Tomcat3.1 介绍3.2 Tomcat的安装与配置3.3 Tomcat的项…

SQLServer 不允许保存更改(主键)

在我们进行数据库表格编辑的时候,往往会出现同一个名字,就比如我们的账号一样,我们在注册自己QQ的时候,我们通常注册过的账号,别人就不能注册了,这是为了保证严密性 所以我们需要点击表格>右键>设计 点击某一列>右键>设计主键 当我们Ctrls 保存的时候回弹出下…

【hot100】刷题记录(6)-轮转数组

题目描述&#xff1a; 给定一个整数数组 nums&#xff0c;将数组中的元素向右轮转 k 个位置&#xff0c;其中 k 是非负数。 示例 1: 输入: nums [1,2,3,4,5,6,7], k 3 输出: [5,6,7,1,2,3,4] 解释: 向右轮转 1 步: [7,1,2,3,4,5,6] 向右轮转 2 步: [6,7,1,2,3,4,5] 向右轮转…

(非技术)从一公里到半程马拉松:我的一年跑步经历

在24年初&#xff0c;从来不运动的我&#xff0c;连跑步一公里都不能完成。而在一年之后的2025年的1月1日&#xff0c;我参加了上海的蒸蒸日上迎新跑&#xff0c;完成了半程马拉松。虽然速度不快&#xff0c;也并不是什么特别难完成的事情&#xff0c;但对我来说还是挺有意义的…

知识蒸馏技术原理详解:从软标签到模型压缩的实现机制

知识蒸馏是一种通过性能与模型规模的权衡来实现模型压缩的技术。其核心思想是将较大规模模型&#xff08;称为教师模型&#xff09;中的知识迁移到规模较小的模型&#xff08;称为学生模型&#xff09;中。本文将深入探讨知识迁移的具体实现机制。 知识蒸馏原理 知识蒸馏的核心…

Linux——冯 • 诺依曼体系结构

目录 一、冯•诺依曼体系结构原理二、内存提高冯•诺依曼体系结构效率的方法三、当用QQ和朋友聊天时数据的流动过程四、关于冯诺依曼五、总结 我们常见的计算机&#xff0c;如笔记本。我们不常见的计算机&#xff0c;如服务器&#xff0c;大部分都遵守冯诺依曼体系 流程&#…

JavaScript - Web APIs(下)

日期对象 目标&#xff1a;掌握日期对象&#xff0c;可以让网页显示日期 日期对象&#xff1a;用来表示时间的对象 作用&#xff1a;可以得到当前系统时间 学习路径&#xff1a; 实例化 日期对象方法 时间戳 实例化 目标&#xff1a;能够实例化日期对象 在代码中发…

【make】makefile变量全解

目录 makefile简介变量全解变量基础变量高级使用1. 将变量里的值进行替换后输出2. 使用变量的嵌套使用3. $ 可以组合使用 override 指示符目标指定变量模式变量 总结参考链接 makefile简介 makefile 是一种类似shell的脚本文件&#xff0c;需要make工具进行解释 makefile 内的语…

51单片机入门_02_C语言基础0102

C语言基础部分可以参考我之前写的专栏C语言基础入门48篇 以及《从入门到就业C全栈班》中的C语言部分&#xff0c;本篇将会结合51单片机讲差异部分。 课程主要按照以下目录进行介绍。 文章目录 1. 进制转换2. C语言简介3. C语言中基本数据类型4. 标识符与关键字5. 变量与常量6.…

常见的同态加密算法收集

随着对crypten与密码学的了解&#xff0c;我们将逐渐深入学习相关知识。今天&#xff0c;我们将跟随同态加密的发展历程对相关算法进行简单的收集整理 。 目录 同态加密概念 RSA算法 ElGamal算法 ELGamal签名算法 Paillier算法 BGN方案 Gentry 方案 BGV 方案 BFV 方案…

aws(学习笔记第二十六课) 使用AWS Elastic Beanstalk

aws(学习笔记第二十六课) 使用aws Elastic Beanstalk 学习内容&#xff1a; AWS Elastic Beanstalk整体架构AWS Elastic Beanstalk的hands onAWS Elastic Beanstalk部署node.js程序包练习使用AWS Elastic Beanstalk的ebcli 1. AWS Elastic Beanstalk整体架构 官方的guide AWS…

FastAPI + GraphQL + SQLAlchemy 实现博客系统

本文将详细介绍如何使用 FastAPI、GraphQL&#xff08;Strawberry&#xff09;和 SQLAlchemy 实现一个带有认证功能的博客系统。 技术栈 FastAPI&#xff1a;高性能的 Python Web 框架Strawberry&#xff1a;Python GraphQL 库SQLAlchemy&#xff1a;Python ORM 框架JWT&…

C语言连接Mysql

目录 C语言连接Mysql下载 mysql 开发库 方法介绍mysql_init()mysql_real_connect()mysql_query()mysql_store_result()mysql_num_fields()mysql_fetch_fields()mysql_fetch_row()mysql_free_result()mysql_close() 完整代码 C语言连接Mysql 下载 mysql 开发库 方法一&#xf…