【C++算法图解专栏】一篇文章带你掌握前缀和算法(一维+二维)

news2024/12/27 12:07:53

✍个人博客:https://blog.csdn.net/Newin2020?spm=1011.2415.3001.5343
📣专栏定位:为 0 基础刚入门数据结构与算法的小伙伴提供详细的讲解,也欢迎大佬们一起交流~
📚专栏地址:https://blog.csdn.net/Newin2020/article/details/126445229
❤️如果有收获的话,欢迎点赞👍收藏📁,您的支持就是我创作的最大动力💪
🎏唠叨唠叨:在这个专栏里我将会整理 PAT 甲级的真题题解,并将他们进行分类,方便大家参考。

前缀和

在有些题目中,需要我们快速的获得一个区间值的和,如果每次查询都循环一个个加的话,时间复杂度会比较大,这时候就要用到前缀和算法,查询区间和的时候,时间复杂度只有 O(1),废话少说,直接上图解。

一维前缀和

首先,我们来看到一维前缀和的模板题,以题带图解的模式带大家深入理解。

输入一个长度为 n 的整数序列。

接下来再输入 m 个询问,每个询问输入一对 l,r。

对于每个询问,输出原序列中从第 l 个数到第 r 个数的和。

输入格式

第一行包含两个整数 n 和 m。

第二行包含 n 个整数,表示整数数列。

接下来 m 行,每行包含两个整数 l 和 r,表示一个询问的区间范围。

输出格式

共 m 行,每行输出一个询问的结果。

数据范围

1≤l≤r≤n,
1≤n,m≤100000,
−1000≤数列中元素的值≤1000

输入样例:

5 3
2 1 3 6 4
1 2
1 3
2 4

输出样例:

3
6
10

该题需要我们查询一段区间的和,我们看来看看如何用前缀和来做,首先来看看原理。

前缀和需要用到一个数组 sum,其中 sum[i] 存储的是 a[1]a[i] 元素值的和,这样只要将该数组计算出来,后续计算的时候就只需 O(1) 的时间复杂度。在代码实现时,我们可以用循环来计算前缀和,用上一轮的计算结果加到本轮当中:

s u m [ i ] = s u m [ i − 1 ] + a [ i ] = a [ 1 ] + . . . + a [ i ] sum[i]=sum[i-1]+a[i]=a[1]+...+a[i] sum[i]=sum[i1]+a[i]=a[1]+...+a[i]

为了方便理解, 假设我们要查询 [3,6] 区间的和,按照上述原理给出对应 sum 中储存的值。

s u m [ 2 ] = a [ 1 ] + a [ 2 ] sum[2]=a[1]+a[2] sum[2]=a[1]+a[2]

s u m [ 3 ] = a [ 1 ] + a [ 2 ] + a [ 3 ] sum[3]=a[1]+a[2]+a[3] sum[3]=a[1]+a[2]+a[3]

s u m [ 6 ] = a [ 1 ] + a [ 2 ] + a [ 3 ] + a [ 4 ] + a [ 5 ] + a [ 6 ] sum[6]=a[1]+a[2]+a[3]+a[4]+a[5]+a[6] sum[6]=a[1]+a[2]+a[3]+a[4]+a[5]+a[6]

我们只需计算 sum[6]-sum[2] 的结果即可得到区间 [3,6] 的和,注意这里是减去 sum[2] 而不是 sum[3],因为 sum[3] 包含了 a[3],这在 [3,6] 区间当中。

a n s = s u m [ 6 ] − s u m [ 2 ] = ( a [ 1 ] + a [ 2 ] + a [ 3 ] + a [ 4 ] + a [ 5 ] + a [ 6 ] ) − ( a [ 1 ] + a [ 2 ] ) = a [ 3 ] + a [ 4 ] + a [ 5 ] + a [ 6 ] ans=sum[6]-sum[2]=(a[1]+a[2]+a[3]+a[4]+a[5]+a[6])-(a[1]+a[2])=a[3]+a[4]+a[5]+a[6] ans=sum[6]sum[2]=(a[1]+a[2]+a[3]+a[4]+a[5]+a[6])(a[1]+a[2])=a[3]+a[4]+a[5]+a[6]

因此,我们可以得到通用公式,查询区间 [l,r] 的和为:

a n s = s u m [ r ] − s u m [ l − 1 ] ans=sum[r]-sum[l-1] ans=sum[r]sum[l1]

实现的时候我们一般从下标 1 开始计算,防止数组越界的情况。

为了加深理解,我们拿题目样例进行模拟,给定一个数组 {2, 1, 3, 6, 4},现在需要查询 [2,4] 区间的和。首先需要计算前缀和,然后通过公式 sum[4]-sum[2-1] 得到结果,如下图所示。

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

int main()
{
    int a[100000], sum[100000];
    int n, m;
    cin >> n >> m;
    for (int i = 1; i <= n; i++)
    {
        scanf("%d", &a[i]);
    }
    //计算前缀和
    for (int i = 1; i <= n; i++)
    {
        sum[i] = sum[i - 1] + a[i];
    }
    //进行查询
    while (m--)
    {
        int l, r;
        scanf("%d %d", &l, &r);
        printf("%d\n", sum[r] - sum[l - 1]);
    }
}

二维前缀和

在有些题目中,需要我们求一片区域中元素值的和,这其实就是一维前缀和的扩展,来看一道模板题。

输入一个 n 行 m 列的整数矩阵,再输入 q 个询问,每个询问包含四个整数 x1,y1,表示一个子矩阵的左上角坐标和右下角坐标。

对于每个询问输出子矩阵中所有数的和。

输入格式

第一行包含三个整数 n,m,q。

接下来 n 行,每行包含 m 个整数,表示整数矩阵。

接下来 q 行,每行包含四个整数 x1,y1,x2,y2,表示一组询问。

输出格式

共 q 行,每行输出一个询问的结果。

数据范围

1≤n,m≤1000,
1≤q≤200000,
1≤x1≤x2≤n,
1≤y1≤y2≤m,
−1000≤矩阵内元素的值≤1000

输入样例:

3 4 3
1 7 2 4
3 6 2 8
2 1 2 3
1 1 2 2
2 1 3 4
1 3 3 4

输出样例:

17
27
21

这其实和一位前缀和十分相似,只不过多了一维,为了方便理解,这次我们先对题目样例进行模拟,题目给定了一个 3×4 的矩阵,如下图所示。

现在需要我们求一片区域中元素值的和,假定给定一个左上角坐标 (2,2),一个右下角坐标 (3,3),则查询的区间如下图所示(红色区域)。

注意,这里的下标是从 1 开始计算,并且原点在左上角,即 (2,2) 是第二行第二个值 6,而 (3,3) 是第三行第三个值 2。

可以看到,一维数组的前缀和无法查询出上述情况,这就要用到二维数组了,现在我们再来看看原理。

计算

定义一个二维前缀和数组 s,其中 s[i][j] 表示从左上角 (1,1) 开始到右下角 (i,j) 这一片矩形中元素值的和。这时,我们在计算前缀和的时候就不像一维一样简洁了,需要考虑到区间重复问题,我先给出二维前缀和的计算公式:

s [ i ] [ j ] = s [ i − 1 ] [ j ] + s [ i ] [ j − 1 ] − s [ i − 1 ] [ j − 1 ] + a [ i ] [ j ] s[i][j]=s[i-1][j]+s[i][j-1]-s[i-1][j-1]+a[i][j] s[i][j]=s[i1][j]+s[i][j1]s[i1][j1]+a[i][j]

不要着急,我们一步步来解析,假设现在求的是 s[3][3],其运算公式如下:

s [ 3 ] [ 3 ] = s [ 2 ] [ 3 ] + s [ 3 ] [ 2 ] − s [ 2 ] [ 2 ] + a [ 3 ] [ 3 ] s[3][3]=s[2][3]+s[3][2]-s[2][2]+a[3][3] s[3][3]=s[2][3]+s[3][2]s[2][2]+a[3][3]

其覆盖的范围如下图所示(红色区域)。

获得该区域的和需要用到之前计算出的两个区域和一个值 a[i][j],而这两个区域分别是 s[i-1][j]s[i][j-1]s[2][3]s[3][2]

但如果将这两个区域的和加起来,可以发现出现了重复的区域(上图绿色区域)即该区域计算了两次。故在计算的时候,我们还需要减去一次重复区域即减去 s[i-1][j-1]s[2][2] 就是最后的结果。

查询

再来看查询的情况,查询的计算公式其实和上面相反,运算的时候同样会遇到重复区域,只不过这里是多减了一个重复区域,需要运算的时候加上。假设左上角坐标为 (x1,y1),右下角为 (x2,y2),可以得到如下公式:

a n s = s [ x 2 ] [ y 2 ] − s [ x 1 − 1 ] [ y 2 ] − s [ x 2 ] [ y 1 − 1 ] + s [ x 1 − 1 ] [ y 1 − 1 ] ans=s[x2][y2]-s[x1-1][y2]-s[x2][y1-1]+s[x1-1][y1-1] ans=s[x2][y2]s[x11][y2]s[x2][y11]+s[x11][y11]

我们来看开头举的那个例子,左上角坐标为 (2,2),右下角坐标为 (3,3),所以运算公式如下:

a n s = s [ 3 ] [ 3 ] − s [ 1 ] [ 3 ] − s [ 3 ] [ 1 ] + s [ 1 ] [ 1 ] ans=s[3][3]-s[1][3]-s[3][1]+s[1][1] ans=s[3][3]s[1][3]s[3][1]+s[1][1]

其中 s[1][1] 就是重复的区域(上图绿色区域),要补上这多减的一次,从而得到最终结果。

代码
#include<bits/stdc++.h>
using namespace std;

int n, m, q;
int a[1005][1005];
int s[1005][1005];
int main()
{
    scanf("%d%d%d", &n, &m, &q);
    //计算前缀和
    for (int i = 1; i <= n; i++)
    {
        for (int j = 1; j <= m; j++)
        {
            scanf("%d", &a[i][j]);
            s[i][j] = s[i - 1][j] + s[i][j - 1] - s[i - 1][j - 1] + a[i][j];
        }
    }
    //进行查询
    while (q--)
    {
        int x1, x2, y1, y2;
        scanf("%d%d%d%d", &x1, &y1, &x2, &y2);
        printf("%d\n", s[x2][y2] - s[x1 - 1][y2] - s[x2][y1 - 1] + s[x1 - 1][y1 - 1]);
    }
    return 0;
}

总结

恭喜您成功点亮前缀和算法技能点!

一维前缀和查询的时候,我们需要注意其边界,当查询区间 [l,r] 的和时,减去的是 sum[l-1] 而不是 sum[l],因为 sum[l] 包含了 a[l]

二维前缀和计算及查询的时候,我们需要注意其重复区域,计算时重复区域被加了 2 次,故要减去 1 次;而查询时重复区域被减了 2 次,故要加上 1 次。

另外,还要注意我们上面建立的坐标系和正常见到的坐标系不同,这里是倒过来的,即 x 轴往由延伸,y 轴往下延伸,这样方便我们进行计算。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Leetcode.1824 最少侧跳次数

题目链接 Leetcode.1824 最少侧跳次数 题目描述 给你一个长度为 n的 3 跑道道路 &#xff0c;它总共包含 n 1个 点 &#xff0c;编号为 0 到 n 。一只青蛙从 0 号点第二条跑道 出发 &#xff0c;它想要跳到点 n处。然而道路上可能有一些障碍。 给你一个长度为 n 1的数组 ob…

ESP32设备驱动-DHT22数字温度湿度传感器驱动

DHT22数字温度湿度传感器驱动 1、DHT22介绍 DHT22电容式湿度传感数字温湿度模块是一款包含复合已校准数字信号输出的温湿度传感器。 应用了专用的数字模块采集技术和温湿度传感技术,确保产品具有高可靠性和优异的长期稳定性。 该传感器包括一个电容式传感器湿元件和一个高精…

Word2Vec与文章相似度

2.7 Word2Vec与文章相似度 学习目标 目标 知道文章向量计算方式了解Word2Vec模型原理知道文章相似度计算方式应用 应用Spark完成文章相似度计算 2.7.1 文章相似度 在我们的某项目推荐中有很多地方需要推荐相似文章&#xff0c;包括首页频道可以推荐相似的文章&#xff0c;详情…

详解Map和Set

目录 一、二叉搜索树 1、概述 2、模拟实现搜索二叉树 a、向搜索二叉树中插入数据 b、查找二叉搜索树的指定值的结点 c、删除二叉树的指定值的结点 3、对二叉搜索树进行性能分析 二、Map的使用 1、Map简介 2、Map常用方法 ​编辑三、Set的使用 1、Set简介 2、S…

零基础学习笔记 - ADF4159

目录1.准备工作1.1.前言1.2.资料1.3.介绍1.4.应用1.5.应用电路2.ADF41592.1.功能框图2.2.通信协议时序2.2.寄存器2.2.0.注意2.2.1.延迟寄存器(R7)映射2.2.2.步进寄存器(R6)映射2.2.3.偏差寄存器(R5)映射2.2.4.时钟寄存器(R4)映射2.2.5.功能寄存器(R3)映射2.2.6.R分频器寄存器(R…

Batchsize的大小怎样设置?Batchsize过大和过小有什么影响

一、Batchsize基本介绍 1. Batchsize是什么 batch_size:表示单次传递给程序用以训练的数据(样本)个数。如果我们的数据集钟含有的样本总数为12800个样本,batch_size=128,那么就需要10个batch才能够训练完一个epoch。 batch_size一般取值为2的N次幂的形式,这是因为CPU或…

高级性能测试系列《38.Arrivals Thread Group、ConcurrencyThread Group、终极线程组》

一、面向目标&#xff1a;Arrivals Thread Group需求&#xff1a;要做一个秒杀&#xff0c; 能支持1000个人同时秒杀&#xff0c;我们的系统不能崩溃。错误案例示范1秒内的人数的运行是有先后的&#xff0c;1000个人在1秒钟内启动&#xff0c;运行完毕一次就停掉了。由图可以看…

Cadence PCB仿真使用Allegro PCB SI查看仿真波形的方法图文教程

🏡《Cadence 开发合集目录》   🏡《Cadence PCB 仿真宝典目录》 目录 1,概述2,拓扑提取阶段仿真方法3,图纸设计阶段仿真方法4,总结1,概述 本文简单介绍使用Alegro PCB SI执行仿真查看仿真波形的两种方法。 2,拓扑提取阶段仿真方法 如下图在拓扑提取阶段,添加完激励…

走进后端开发流程 | 青训营笔记

目录 一、走进后端开发流程 1、传统流程 2、敏捷开发 3、SAFe简介 4、字节团队的开发流程 二、开发流程详解 1、需求阶段 2、开发阶段 云原生开发 团队的分支策略 自测 3、测试阶段 4、发布阶段 简单发布 金丝雀发布 滚动发布&#xff08;推荐&#xff09; 蓝…

记录每日LeetCode 160.相交链表 Java实现

题目描述&#xff1a; 给你两个单链表的头节点 headA 和 headB &#xff0c;请你找出并返回两个单链表相交的起始节点。如果两个链表不存在相交节点&#xff0c;返回 null 。 图示两个链表在节点 c1 开始相交&#xff1a; 题目数据 保证 整个链式结构中不存在环。 注意&…

进程和线程

1.关于进程进程基本概念进程&#xff08;process&#xff09;也叫任务&#xff08;task&#xff09;。进程是操作系统对一个正在运行的程序的一种抽象。也就是说&#xff0c;可以把进程看作程序的一次运行过程。 &#xff08;一个正在运行的程序——>进程。没有跑起来就不算…

CSS语法与CSS选择器

目录 CSS 语法 实例 例子解释 CSS 选择器 CSS 元素选择器 实例 CSS id 选择器 实例 CSS 类选择器 实例 实例 实例 CSS 通用选择器 实例 CSS 分组选择器 实例 所有简单的 CSS 选择器 延伸阅读 CSS 语法 CSS 规则集&#xff08;rule-set&#xff09;由选择器和…

java spring IOC外部Bean注入

外部Bean注入也是一种Bean操作的属性注入 但这次我们要注入的是一个类对象 我们先创建spring项目 引入基本依赖 然后在src下创建两个包 gettingStarted 和 generate 这个名字可以随便取 但和我同名 可以让你们不会出现 名称不一样导致资源找不到的问题 然后在 gettingStarte…

【寒假每日一题】AcWing 4729. 解密(补)

文章目录一、题目1、原题链接2、题目描述二、解题报告1、思路分析2、时间复杂度3、代码详解三、知识风暴韦达定理及其逆定理一、题目 1、原题链接 4729. 解密 2、题目描述 给定一个正整数 k&#xff0c;有 k次询问&#xff0c;每次给定三个正整数 ni,ei,di&#xff0c;求两个正…

腾讯云GPU服务器环境部署与连接配置

先前博主购买了腾讯云的GPU服务器后&#xff0c;发现上面预装的环境存在一些问题&#xff0c;因此便来重新部署一下。 为了操作方便&#xff0c;博主这里使用了一个远程控制端软件&#xff1a;Xshell 博主在初始化时已经安装过pytorch了&#xff0c;我们首先看看安装的路径 测…

python winio的驱动级按键模拟

一&#xff0c;环境准备 电脑进入BIOS中关闭安全启动项菜单 电脑需要配备PS2接口的鼠标和键盘 二&#xff0c;安装rabird.winio环境 1、终端下执行pip install rabird.winio 然后重启电脑进入高级启动&#xff08;禁止驱动程序强制签名&#xff09;&#xff0c;这个方法网上…

探索SpringMVC-DispatcherServlet

前言 在《探索SpringMVC-web上下文》中&#xff0c;我们介绍了DispatcherServlet的上下文的初始化。然后为了让大家对DispatcherServlet的各个组件有所了解&#xff0c;我们花了很多的时间来介绍各大组件。现在我们来看看DispatcherServlet是如何使用这些组件完成功能的。 Di…

【前端杂货铺】一个普通人在CSDN创作的一周年

个人简介 &#x1f440;个人主页&#xff1a; 前端杂货铺 &#x1f64b;‍♂️学习方向&#xff1a; 主攻前端方向&#xff0c;也会涉及到服务端 &#x1f4c3;个人状态&#xff1a; 在校大学生一枚&#xff0c;已拿多个前端 offer&#xff08;秋招&#xff09; &#x1f680;未…

Python---列表和元组

专栏&#xff1a;python 个人主页&#xff1a;HaiFan. 专栏简介&#xff1a;本专栏主要更新一些python的基础知识&#xff0c;也会实现一些小游戏和通讯录&#xff0c;学时管理系统之类的&#xff0c;有兴趣的朋友可以关注一下。 列表和元组前言列表的的概念列表的创建访问下标…

【微服务】Eureka注册中心

本系列介绍的是Spring Cloud中涉及的知识点&#xff0c;如有错误欢迎指出~ 一.引子 假如我们的服务提供者user-service部署了多个实例&#xff0c;如图&#xff1a; 大家思考几个问题&#xff1a; 问题一&#xff1a;order-service在发起远程调用的时候&#xff0c;该如何得知…