前缀和算法第一弹(一维前缀和和二维前缀和)

embedded/2025/3/17 18:18:15/

目录

前言

1. 一维前缀和

(1)题目及示例

​编辑

(2)暴力解法

(3)算法优化

2. 二维前缀和

(1)题目及示例

(2)暴力解法

(3)算法优化


 

前言

本文将从解决几道线上OJ题目,带你了解前缀和算法。从暴力解法讲起,进一步优化成最佳解法。


1. 一维前缀和

(1)题目及示例

题目链接:【模板】前缀和_牛客题霸_牛客网

 如下图所示,第一行输入两个整数n和q,分别是3和2。其中n等于3,表示该数组中有三个数,第二行也会输入三个整数。q等于2,表示有两个查询,向第三行和第四行输入两个数字。

我们以第一次查询为例,输入为1和2,表示从数组第一个数加到第二个数,就是1加2等于3.

(2)暴力解法

对于这道题目,我们使用最直接的办法就是在一次查询中,输入两个数字x和y,直接从数组第x个元素开始一直加到第样个元素。

分析一下时间复杂度,假设数组元素个数是n,每次都要遍历完整个数组,也就是遍历n次,还有q次查询,那么时间复杂度就是O(n*q)。

代码如下,需要注意的是n的上限是10^5,而每个元素最大值是10^9,那么前缀和最大值是10^14,已经大于普通四字节整数的大小,需要使用long long64位整数。

#include <iostream>
#include <vector>
using namespace std;int main() {int n, q;while (cin >> n >> q) {vector<int> nums(n);for (int i = 0; i < n; i++) cin >> nums[i];while (q--) {int l, r;cin >> l >> r;long long ret = 0;for(int i = l-1; i < r; i++)ret += nums[i];// 打印区间和cout << ret << endl;}}return 0;
}

(3)算法优化

如果使用暴力解法,时间复杂度是O(n*q),效率一般。我们可以使用前缀和来优化效率。

前缀和是快速求出数组中某一个连续区间的和。

  • 如下图所示,arr数组里面有8个元素。我们创建一个dp数组,内部9个元素全为0。dp数组大小比arr数组多一个,这是因为数组的下标其实从0开始,而下面的index索引是从1开始,方便构建前缀和数组。
  • dp数组中第i个下标元素,表示arr数组index索引从1到i元素之和。因此,dp数组中第i个下标元素等于前一个元素加上arr数组中index索引为i的元素,可以通过一个式子dp[i]=dp[i-1]+arr[i-1],其中index索引比数组下标大1。

 在一轮查询中,输入两个输L和R,表示第L个元素到第 R个元素之间的和。如下图,相当于R的前缀和减去L-1的前缀和,即dp[R]-dp[L-1]。

#include <iostream>
#include <vector>
using namespace std;int main() {int n, q;while (cin >> n >> q) {vector<int> arr(n);for (int i = 0; i < n; i++) cin >> arr[i];// 构建前缀和表vector<long long> dp(n + 1, 0); // 使用n+1个元素初始化为0for (int i = 1; i <= n; i++) { dp[i] = dp[i - 1] + arr[i - 1];}while (q--) {int l, r;cin >> l >> r;// 输出区间和,注意l和r是1-based索引cout << dp[r] - dp[l - 1] << endl;}}return 0;
}

分析一下时间复杂度,一开始需要构建前缀和表,遍历整个数组,相当于n次遍历。之后,有q次查询,每次查询不需要遍历数组,时间复杂度相当于O(1)。因此,总的时间复杂度是O(n+q)。

2. 二维前缀和

(1)题目及示例

题目链接:【模板】二维前缀和_牛客题霸_牛客网

如下图所示,第一行先输入两个数字式n和m,n为3,m为4。表示一个三行四列的矩阵,也是一个二维数组。第一行中最后一个数是3,表示有三次查询输入。

我们以第二次查询,即第六行输入为例。图中右边是一个三行四列的矩阵,第六行输入前两个参数是x1和y1,表示矩阵中第一行第一列的参数。后两个参数是x2和y2,表示第三行第三列的参数。题目中要求这两个参数为对顶点,由此框出来的黑色矩阵中所有元素之和。

(2)暴力解法

如果使用最直接的解法,根据题目给出的查询参数x1,y1,x2和y2,以此确定矩阵中求和范围,直接遍历数组进行求和。

分析时间复杂度,假设是n行m列的矩阵,每次都要遍历完整个数组,那么一次查询时间复杂度是O(n*m)。如果有q次查询,那么总的时间复杂度是O(n*m*q)

如果测试矩阵非常大,查询次数非常多,可能会超时。

(3)算法优化

针对矩阵,或者说二维数组,我们可以使用二维前缀和。前缀和是数组中某个连续区间的和。arr数组是题目给出的二维矩阵,dp数组存储的就是前缀和。如下图,dp二维数组中宏方块位置的前缀和,是arr二维数组红色斜线位置的区域。

所以在进行查询之前,我们需要构建二维数组的前缀和。

  • 如下图,我们把任意一个元素的前缀和抽象成为一个黑色正方形,对黑色正方形内部所有元素求和,就是该位置的前缀和。假设这是第x行第y列元素的前缀和,可以把该正方形切割成四个区域。
  • A区域是第x-1行第y-1列元素的前缀和,但是B和C区域的元素和不好求。我们用A和B结合在一起,A和C结合在一起,分别是蓝色斜线的区域和红色斜线区域。
  • 蓝色斜线区域是第x-1行第y列元素的前缀和,红色斜线区域是第x行第y-1列元素的前缀和。而D就是该元素。

构建完前缀和表后,我们需要使用前缀和表来快速求出题目给出连续区间的和。

  • 假设题目给出的参数是x1,y1,x2和y2。即第x1行第y1列元素和第x2行第y2列元素。
  • 如下图所示,红色D区域就是所求元素和区域。我们可以把该区间分为四个区域。构建前缀和表时,我们知道下面四个区域之和就是(x2,y2)元素的前缀和,即dp[x2][y2]。
  • A区域是(x1-1,y1-1)元素的前缀和,而B区域和C区域不能直接使用前缀和表示。我们可以使用A与这两个区域结合。
  • 那么A和B区域就是(x1-1,y2)元素的前缀和,A和C区域就是(x2,y1-1)元素的前缀和。
  • 使用dp[x2][y2]减去A+B区域和A+C区域时,记得多加一个A区域前缀和。

如下是代码部分,构建前缀和表时,通常会把行和列多加一个,方便使用。

#include <iostream>
#include <vector>using namespace std;int main() 
{// 1.读数据int n = 0, m = 0, q = 0;while(cin >> n >> m >> q){// 构建数组vector<vector<int>> nums(n + 1, vector<int>(m + 1));for(int i = 1; i <= n; i++){for(int j = 1; j <= m; j++)cin >> nums[i][j];}// 2. 构造二维前缀和矩阵vector<vector<long long>> dp(n + 1, vector<long long>(m + 1));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] + nums[i][j] - dp[i-1][j-1];}vector<int> ret;int x1 = 0, y1 = 0, x2 = 0, y2 = 0;while(q--){cin >> x1 >> y1 >> x2 >> y2;// 求D区域元素和cout << dp[x2][y2] - dp[x1-1][y2] - dp[x2][y1 - 1] + dp[x1-1][y1-1] << endl; }}
}

分析一下时间复杂度,构建二维前缀和表,需要遍历一边二维数组,时间复杂度是O(n*m)。查询一次,不需要遍历数组,时间复杂度是O(1)。如果有q次查询,那么总的时间复杂度是O(n*m+q)。


对于其他关于求某个连续区间和的题目,需要根据具体问题来构建适合的前缀和表或者后缀和表。

创作充满挑战,但若我的文章能为你带来一丝启发或帮助,那便是我最大的荣幸。如果你喜欢这篇文章,请不吝点赞、评论和分享,你的支持是我继续创作的最大动力!


http://www.ppmy.cn/embedded/173391.html

相关文章

vscode接入DeepSeek 免费送2000 万 Tokens 解决DeepSeek无法充值问题

1. 在vscode中安装插件 Cline 2.打开硅基流动官网 3. 注册并登陆&#xff0c;邀请码 WpcqcXMs 4.登录后新建秘钥 5. 在vscode中配置cline (1) API Provider 选择 OpenAI Compatible &#xff1b; (2) Base URL设置为 https://api.siliconflow.cn](https://api.siliconfl…

MySQL时间溢出原理、影响与解决方案

一、问题背景与现象复现 操作场景&#xff1a; 本文将手把手带您了解mysql时间溢出原理、实战影响与全面解决方案&#xff0c;所有代码均通过dblens for mysql数据库工具验证&#xff0c;推荐使用该工具进行可视化数据库管理和开发。 在MySQL 5.7环境中&#xff0c;若通过命令…

JetBrains(全家桶: IDEA、WebStorm、GoLand、PyCharm) 2024.3+ 2025 版免费体验方案

JetBrains&#xff08;全家桶: IDEA、WebStorm、GoLand、PyCharm&#xff09; 2024.3 2025 版免费体验方案 前言 JetBrains IDE 是许多开发者的主力工具&#xff0c;但从 2024.02 版本起&#xff0c;JetBrains 调整了试用政策&#xff0c;新用户不再享有默认的 30 天免费试用…

【Maven教程与实战案例】

文章目录 前言一、Maven是什么&#xff1f;二、Maven的安装与配置1. 安装前置条件2. 下载与配置 Maven3. 验证安装 三、Maven的核心概念1. POM.xml 文件2. 构建生命周期与插件机制 四、实战项目示例1. 项目目录结构2. 编写代码App.javaAppTest.java 3. 构建项目4. 运行项目 前言…

应用层之网络应用模型,HTTP/HTTPS协议

应用层是网络协议栈的最顶层&#xff0c;直接为应用程序提供通信服务&#xff0c;定义了不同主机间应用进程交互的规则&#xff0c;包括报文类型、语法、语义及通信时序 一、网络应用模型 1.定义及特点 模型定义核心特点典型应用场景C/S客户端向服务器发起请求&#xff0c;服…

工厂变电所运维云平台解决方案-直击运维痛点,重塑高效安全运维典范

1、概述 变电所运维云平台可以看做是电力监控系统的网络应用延伸&#xff0c;变电所运维云平台通过互联网&#xff0c;电力运维人员通过手机可以随时随地了解工厂配电系统的运行情况&#xff0c;做到无人值守或者少人值守&#xff0c;同时可以监测用能状况、漏电、线缆异常发热…

[特殊字符] 深度实战:Android 13 系统定制之 Recovery 模式瘦身指南

&#x1f31f; 核心需求 在 Android 13 商显设备开发中&#xff0c;需精简 Recovery 模式的菜单选项&#xff08;如Reboot to bootloader/Enter rescue&#xff09;&#xff0c;但直接修改g_menu_actions后在User 版本出现黑屏卡死问题&#xff0c;需综合方案解决。 &#x1f5…

OpenCV(应用) —— 凸包检测的实战应用

文章目录 一、凸包的概念二、Opencv中的API三、应用场景与实战3.1、实战场景一一、凸包的概念 常见的找寻目标的外轮廓有矩形框(如最小外接矩阵)和圆形框,但这种包围框为了保持几何形状,与图形的真实轮廓贴合度较差。如果能找出图形最外层的端点,将这些端点连接起来,就可…