前缀和篇——繁星斗斗数字交织中,觅得效率明月辉光(1)

news2025/2/5 15:43:42

在这里插入图片描述

前言

在这片无边无际的数字海洋中,如何从中提取出有价值的讯息,成为了计算机科学中的一项重要课题。前缀和算法,作为一种巧妙的技术,恰如其名——通过计算序列中各个元素的前缀和,能够为我们提供一种高效的查询方式,避免了重复计算的复杂度。在这篇博客中,我们将一起探索前缀和算法的奥秘,揭开它在数据处理中的应用与优势。

一. 算法简介与原理分析

1.1 原理讲解

前缀和算法的核心思想是:***对于一个数组,预先计算出数组从第一个元素到当前元素的累积和。这种累积和的结果,能够帮助我们快速计算出数组任意区间内的和。** 举个简单的例子,假设有一个数组 A,其元素为:
A = [a1, a2, a3, ..., an]

那么,前缀和数组 S 就是一个满足如下关系的数组:

S[i] = A[1] + A[2] + ... + A[i](1 <= i <= n)

通过这个预处理步骤,我们能够将任何区间 [l, r] 内的和,通过以下公式迅速计算得到:

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

这一公式的妙处在于,无论查询的区间多么广泛,计算时间始终是常数级别的 O(1),只需要在预处理阶段做 O(n) 的计算。

1.2 应用方面

前缀和算法在多个领域都得到了广泛的应用,尤其是在处理数组、字符串等数据结构时,展现出了巨大的优势。以下是几种典型的应用场景:

  • 区间求和问题:
    这是前缀和算法最常见的应用。在给定一个数组时,我们常常需要求解数组中某一范围内所有元素的和。通过前缀和算法,可以将时间复杂度从 O(n) 降低到 O(1)。

  • 数组的频率统计:
    如果我们对数组中的某些元素出现的频率感兴趣,前缀和算法也可以轻松处理。例如,统计一个数组中,某个数值在前缀中的出现次数。

  • 二维数组的区域查询:
    扩展到二维数组时,前缀和算法同样表现出了强大的能力。通过二维前缀和数组,可以在常数时间内查询任意矩形区域的和。

  • 字符串匹配与模式识别:
    在字符串处理中,前缀和可以用来快速计算子串的哈希值,从而加速字符串匹配算法。

1.3 优劣势分析

优势:

  • 时间效率高:
    前缀和算法的预处理时间是 O(n),而查询区间和的时间复杂度是 O(1),在需要大量查询时,能显著提升效率。

  • 空间效率适中:
    前缀和算法的空间复杂度为 O(n),对于大多数情况来说,这个空间开销是可以接受的。

劣势:

  • 只能处理静态问题:
    前缀和算法的局限性在于它仅适用于静态数组。对于动态数组(例如频繁更新的数组),前缀和需要进行复杂的重新计算,效率会大打折扣。

  • 不适用于非线性数据结构:
    前缀和算法主要针对一维和二维数组等线性数据结构,对于树、图等非线性数据结构的处理,前缀和的优势不明显。

1.4 改进拓展

前缀和算法的改进与扩展 在一些特殊场景下,前缀和算法也有不少改进和扩展的方案。例如:
  • 差分数组: 在处理区间更新问题时,可以结合差分数组与前缀和算法,在 O(1) 时间内进行区间更新。

  • 二维前缀和: 对于二维数组,前缀和算法的扩展可以帮助我们高效地查询任意矩形区域的和,常见于图像处理等领域。

  • 树状数组与线段树: 在需要支持动态更新的情况下,树状数组和线段树是前缀和算法的常见替代品,能够在动态环境中提供类似的查询和更新效率。

下面我们结合具体题目进行理解运用。

二. 【模板】前缀和

2.1 题目链接:

https://www.nowcoder.com/practice/acead2f4c28c401889915da98ecdc6bf?tpId=230&tqId=2021480&ru=/exam/oj&qru=/ta/dynamic-programming/question-ranking&sourceUrl=%2Fexam%2Foj%3Fpage%3D1%26tab%3D%25E7%25AE%2597%25E6%25B3%2595%25E7%25AF%2587%26topicId%3D196

2.2 题目分析:

该题是典型的IO型题目(输入输出),下面我们来分析具体输入输出要求:

  1. 第一行内输入两个正整数,n表示数组长度,q表示要输出的行数
  2. 第二行内需要输入n个整数,即数组内的各个元素
  3. 接下来需要输入q行,每一行输入两个数,分别表示计算和的起点和终点。
    注意:此时的起点终点指的是第i个数,而非元素的下标!!!
  4. 输出q行,每一行为该区间内元素的和

示例如图:
在这里插入图片描述

2.3 算法讲解:

暴力解法(会超时):
该题的思路不难,可以采取每次逐个遍历该区间累加的方式进行输出,但是如此操作,会造成极大的时间和空间冗余。

前缀和:
. 先预处理出来⼀个「前缀和」数组:

  • ⽤ dp[i] 表⽰: [1, i] 区间内所有元素的和,那么 dp[i - 1] ⾥⾯存的就是 [1, i - 1] 区间内所有元素的和,那么:可得递推公式: dp[i] = dp[i - 1] + arr[i]
  • 使⽤前缀和数组,「快速」求出「某⼀个区间内」所有元素的和:当询问的区间是 [l, r] 时:区间内所有元素的和为: dp[r] - dp[l - 1] 。

2.4 代码实现

#include <iostream>

using namespace std;
const int N=100010;
long long nums[N], sums[N];
int main() {
    int n,p;
    cin>>n>>p;//输入
    
    for(int i=1;i<=n;i++)
    {
        cin>>nums[i];
    }//输入各个元素
    for(int i=1;i<=n;i++)
    {
        sums[i]=sums[i-1]+nums[i];

    }//计算前缀和
    while(p--)
    {
        int l,r;
        cin>>l>>r;
        cout<<sums[r]-sums[l-1]<<endl;
    }
    return 0;

}
// 64 位输出请用 printf("%lld")

三. 【模板】二维前缀和

3.1 题目链接:

https://www.nowcoder.com/practice/99eb8040d116414ea3296467ce81cbbc?tpId=230&tqId=2023819&ru=/exam/oj&qru=/ta/dynamic-programming/question-ranking&sourceUrl=%2Fexam%2Foj%3Fpage%3D1%26tab%3D%25E7%25AE%2597%25E6%25B3%2595%25E7%25AF%2587%26topicId%3D196

3.2 题目分析:

本题为IO型,我们先来具体分析输入输出要求:

  1. 本题存在一个nxm的矩阵,且下标从1开始
  2. 第一行输入三个整数n,m,q,其中n和m表示矩阵规格,q则表示需要做q行输出
  3. 之后输入q行下标,分别为x1,y1,x2,y2,求取以 (x1, y1) 为左上角 , (x2,y2) 为右下角的子矩阵的和
    注意:此处下标的顺序切勿混淆!!!
  4. 输出q行上述和

3.3 思路讲解:

以此图为例,以 (x1, y1) 为左上角 , (x2,y2) 为右下角的子矩阵的和即为D所表示的区域。
在这里插入图片描述
类⽐于⼀维数组的形式,如果我们能处理出来从 [0, 0] 位置到 [i, j] 位置这⽚区域内所有
元素的累加和,就可以在 O(1) 的时间内,搞定矩阵内任意区域内所有元素的累加和。因此我们
接下来仅需完成两步即可:

  • 第⼀步:构建前缀和矩阵
    这⾥就要⽤到⼀维数组⾥⾯的拓展知识,我们要在矩阵的最上⾯和最左边添加上⼀⾏和⼀列
    0,这样我们就可以省去⾮常多的边界条件的处理(可以⾃⾏尝试直接搞出来前缀和矩
    阵,边界条件的处理会令人崩溃)。处理后的矩阵就像这样:
    在这里插入图片描述
    这样,我们填写前缀和矩阵数组的时候,下标直接从 1 开始,能⼤胆使⽤ i - 1 , j - 1 位
    置的值。
    注意 dp 表与原数组 matrix 内的元素的映射关系:
    i. 从 dp 表到 matrix 矩阵,横纵坐标减⼀;
    ii. 从 matrix 矩阵到 dp 表,横纵坐标加⼀。

前缀和矩阵中 sum[i][j] 的含义,以及如何递推⼆维前缀和⽅程:
a. sum[i][j] 的含义:

sum[i][j] 表⽰,从 [0, 0] 位置到 [i, j] 位置这段区域内,所有元素的累加和,即下图红黄绿蓝四个区域相加之和。

在这里插入图片描述
下面我们逐个分析这四个区域:

  • 黄色:即矩阵martix[i-1][j-1],注意映射关系
  • 红色:可直接求出,即sum[i-1][j-1]
  • 蓝色:不好直接求出,但是红+蓝=sum[i-1][j],蓝色只需令和再减去红色即可
  • 绿色:原理同上,红+绿=sum[i][j-1]
综上,sum[i][j]=红+绿+红+蓝+黄-红=sum[i][j-1]+sum[i-1][j]+martix[i-1][j-1]-sum[i-1][j-1].
  • 第⼆步:使⽤前缀和矩阵
    题⽬的接⼝中提供的参数是原始矩阵的下标,为了避免下标映射错误,这⾥直接先把下标映射成
    dp 表⾥⾯对应的下标: row1++, col1++, row2++, col2++

接下来分析如何使⽤这个前缀和矩阵,如下图(注意这⾥的 row 和 col 都处理过了,对应的正
是 sum 矩阵中的下标):
在这里插入图片描述
对于左上⻆ (row1, col1) 、右下⻆ (row2, col2) 围成的区域,正好是红⾊的部分。因
此我们要求的就是红⾊部分的⾯积,继续分析⼏个区域:
i. ⻩⾊,能直接求出来,就是 sum[row1 - 1, col1 - 1] (为什么减⼀?因为要剔除
掉 row 这⼀⾏和 col 这⼀列)
ii. 绿⾊,直接求不好求,但是和⻩⾊拼起来,正好是 sum 表内 sum[row1 - 1][col2]
的数据;
iii. 同理,蓝⾊不好求,但是 蓝 + ⻩ = sum[row2][col1 - 1] ;
iv. 再看看整个⾯积,好求嘛?⾮常好求,正好是 sum[row2][col2] ;
v. 那么,红⾊就 = 整个⾯积 - ⻩ - 绿 - 蓝,但是绿蓝不好求,我们可以这样减:整个⾯积 -(绿

  • ⻩ )-(蓝 + ⻩),这样相当于多减去了⼀个⻩,再加上即可
    综上所述:红 = 整个⾯积 - (绿 + ⻩)- (蓝 + ⻩)+ ⻩,从⽽可得红⾊区域内的元素总和为:
sum[row2][col2]-sum[row2][col1 - 1]-sum[row1 - 1][col2]+sum[row1 - 1][col1 - 1]

3.4 代码实现:

#include <iostream>
using namespace std;
const int N=1010;
int arr[N][N];
long long dp[N][N];//数组与前缀和数组

int main() {
    //输入数据
    int n,m,q;
    cin>>n>>m>>q;
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=m;j++)
        {
            cin>>arr[i][j];
        }
    }
    //处理前缀和数组
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=m;j++)
        {
            dp[i][j]=dp[i-1][j]+dp[i][j-1]+arr[i][j]-dp[i-1][j-1];
        }
    }
    //使用前缀和数组
    while(q--)
    {
        int x1,y1,x2,y2;
        cin>>x1>>y1>>x2>>y2;
        cout<<dp[x2][y2] - dp[x1 - 1][y2] - dp[x2][y1 - 1] + dp[x1 - 1][y1 - 1]<<endl;

    }//输出数据
    return 0;
}
// 64 位输出请用 printf("%lld")

小结

本篇关于前缀和的介绍讲解较为基础,将于后续文章中结合进阶难度的题目循序渐进,希望能对大家的学习产生帮助,欢迎各位佬前来支持斧正!!!
在这里插入图片描述

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

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

相关文章

STM32 HAL库 + LM2904运算放大器 + ADC + VDO温度传感器:电路设计及代码实现

本文将详细介绍如何使用STM32F407的HAL库&#xff0c;实现通过单通道ADC采集VDO温度传感器的信号&#xff0c;并通过串口将采集到的温度值打印输出。具体流程包括&#xff1a;通过分压电阻将获得VDO温度传感器的分压电压&#xff0c;再利用运算放大器LM2904对信号进行放大&…

Python酷库之旅-第三方库Pandas(252)

目录 一、用法精讲 1191、pandas.tseries.offsets.BusinessMonthBegin.n属性 1191-1、语法 1191-2、参数 1191-3、功能 1191-4、返回值 1191-5、说明 1191-6、用法 1191-6-1、数据准备 1191-6-2、代码示例 1191-6-3、结果输出 1192、pandas.tseries.offsets.Busine…

【Linux服务器】CPU问题排查

概述 总体排查思路 总体观察&#xff0c;htop命令观察系统负载程度 检查CPU占用高的进程检查CPU硬件信息是否为问题根源从宏观到细节分析系统IO情况最后检查系统日志 排查思路 系统负载 通过htop命令查看系统负载&#xff0c;通过系统负载可以判断系统是否繁忙&#xff0c;主…

详解MyBatis之篇一

目录 MyBatis 定义 使用MyBatis操作数据库 创建项目 配置 演示 UserInfo.java UserInfoMapper UserInfoMapperTest 数据准备 自动生成测试类 运行结果 MyBatis 定义 MyBatis 是一个优秀的持久层框架&#xff0c;它支持定制化 SQL、存储过程以及高级映射。MyBatis 避…

编译器优化技术

方法内联 逃逸分析 公共子表达式消除 数据边界检查消除

汽车IVI中控开发入门及进阶(三十五):架构QML App Architecture Best Practices

在Qt/QML工程的架构中,架构很重要,虽然本身它有分层,比如QML调用资源文件(图片等)显示GUI界面,后面的CPP文件实现界面逻辑,但是这个分类还有点粗。在实际开发中,界面逻辑也就是基于类cpp的实现,也开始使用各种面向对象的设计模式,实现更加优秀的开发架构,这点尤其在…

本地Docker部署个人在线音乐平台Melody结合内网穿透远程访问听音乐

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

异常--C++

文章目录 一、异常的概念及使用1、异常的概念2、异常的抛出和捕获3、栈展开4、查找匹配的处理代码5、异常重新抛出6、异常安全问题7、异常规范 二、标准库的异常 一、异常的概念及使用 1、异常的概念 异常处理机制允许程序中独立开发的部分能够在运行时就出现的问题进行通信并…

字符串p型编码

字符串p型编码 C 语言实现C 实现Java 实现Python 实现 &#x1f490;The Begin&#x1f490;点点关注&#xff0c;收藏不迷路&#x1f490; 给定一个完全由数字字符&#xff08;‘0’,‘1’,‘2’,…,‘9’&#xff09;构成的字符串str&#xff0c;请写出str的p型编码串。例如&…

UIlicious - 自动化端到端测试

在现代软件开发中&#xff0c;测试自动化已然成为产品交付质量的基石。而端到端测试&#xff08;E2E&#xff09;&#xff0c;作为验证整个应用流畅运行的关键&#xff0c;常常是测试工作中最具挑战性的一环。这时&#xff0c;一款简单高效的自动化测试工具——UIlicious&#…

机器学习:机器学习项目的完整周期

建立一个有价值的机器学习系统时&#xff0c;需要考虑和计划哪些步骤&#xff1f; 以语音识别为例演示机器学习项目的全周期&#xff1a;机器学习项目的第一步是对项目进行范围划分&#xff0c;即决定什么是项目和你想做什么&#xff0c;然后是收集数据&#xff0c;所以决定需…

浪潮X86服务器NF5280、8480、5468、5270使用inter VROC Raid key给NVME磁盘做阵列

Inter VROC技术简介 Intel Virtual RAID on CPU (Intel VROC) 简单来说就是用CPU的PCIE通道给NVME硬盘做Raid 更多信息可以访问官方支持页面 Raid Key 授权&#xff0c;即VROC SKU 授权主要有用的有2个标准和高级&#xff0c;仅Raid1的授权我暂时没见过。 标准 VROCSTANMOD …

ROS基本框架2——在ROS开发中创建并使用自定义消息(C++版本)

ROS基本框架2——在ROS开发中创建并使用自定义消息(C++版本) code review! 参考笔记 1.ROS基本框架1——编写简单的发布者和订阅者(C++和Python版本) 2.ROS基本框架2——在ROS开发中创建并使用自定义消息(C++版本) 文章目录 ROS基本框架2——在ROS开发中创建并使用自定义…

鸿蒙征文|鸿蒙技术分享:使用到的开发框架和技术概览

目录 每日一句正能量前言正文1. 开发环境搭建关键技术&#xff1a;2. 用户界面开发关键技术&#xff1a;3. 应用逻辑开发关键技术&#xff1a;4. 应用测试关键技术&#xff1a;5. 应用签名和打包关键技术&#xff1a;6. 上架流程关键技术&#xff1a;7. 后续维护和更新关键技术…

(长期更新)《零基础入门 ArcGIS(ArcMap) 》实验二----网络分析(超超超详细!!!)

相信实验一大家已经完成了&#xff0c;对Arcgis已进一步熟悉了&#xff0c;现在开启第二个实验 ArcMap实验--网络分析 目录 ArcMap实验--网络分析 1.1 网络分析介绍 1.2 实验内容及目的 1.2.1 实验内容 1.2.2 实验目的 2.2 实验方案 2.3 实验流程 2.3.1 实验准备 2.3.2 空间校正…

go语言 Pool实现资源池管理数据库连接资源或其他常用需要共享的资源

go Pool Pool用于展示如何使用有缓冲的通道实现资源池&#xff0c;来管理可以在任意数量的goroutine之间共享及独立使用的资源。这种模式在需要共享一组静态资源的情况&#xff08;如共享数据库连接或者内存缓冲区&#xff09;下非 常有用。如果goroutine需要从池里得到这些资…

马铃薯病害识别(VGG-16复现)

VGG16-Pytorch实现马铃薯病害识别 &#x1f368; 本文为&#x1f517;365天深度学习训练营 中的学习记录博客 &#x1f356; 原作者&#xff1a;K同学啊 电脑系统&#xff1a;Windows11 显卡型号&#xff1a;NVIDIA Quadro P620 语言环境&#xff1a;python 3.9.7 编译器&am…

HCSIF: 中国区域2000-2022年高时空分辨率(500m)SIF数据集

日光诱导叶绿素荧光&#xff08;Solar-induced chlorophyll fluorescence, SIF&#xff09;被誉为“植被光合作用的探针”。2017年&#xff0c;搭载在Sentinel-5P卫星上的 TROPOMI (TROPOspheric Monitoring Instrument&#xff09;传感器成功发射&#xff0c;该卫星同时具有高…

STL:相同Size大小的vector和list哪个占用空间多?

在C中&#xff0c;vector和list是两种不同的序列容器。vector底层是连续的内存&#xff0c;而list是非连续的&#xff0c;分散存储的。因此&#xff0c;vector占用的空间更多&#xff0c;因为它需要为存储的元素分配连续的内存空间。 具体占用多少空间&#xff0c;取决于它们分…

蓝牙设备驱动开发

文章目录 一、蓝牙协议架构二、蓝牙协议的HCI传输层三、编程框架 一、蓝牙协议架构 蓝牙是无线数据和语音传输的开放式标准&#xff0c;它将各种通信设备、计算机及其终端设备、各种数字数据系统、甚至家用电器采用无线方式联接起来。它的传输距离为10cm&#xff5e;10m&#…