前缀和与经典例题详解

article/2025/10/1 22:50:58

前缀和与及经典例题详解

南昌理工ACM集训队

这是目录

  • 前缀和与及经典例题详解
    • 前言
    • 前缀和
      • P5638 光骓者的荣耀
    • 二维前缀和
      • P2004 领地选择
    • 最大子段和
      • P1115 最大子段和
      • P1719 最大加权矩形
    • 写在最后

前言

下面的例题都出自于洛谷官方算法2-1题单,有兴趣的同学可以去水一水,题目很简单孩子已经吃了两箱了
题单传送门

本人小白如有不对欢迎指正ლ(╹◡╹ლ)

前缀和

前缀和,顾名思义就是从第一个数到第i个数之间所有数的总和。一般用在对数组的预处理,在解决某问题时如果需要多次用到数组区间内的和,用前缀和预处理目标数组会是个不错的选择。

那么前缀和怎么获得呢

前缀和是从第一个数到第i个数之间的总和,所以我们可以利用动态规划的思想,输入数组第i个数的时候只要在输入之后加上i-1的前缀和就可以得到当前第i个数的前缀和。
如下

//设前缀和数组为a[N]
for (int i = 1; i <= N; i++) {cin >> a[i];a[i] = a[i - 1] + a[i];}

那么区间[ l , r ]之间的和怎么获得呢

我们知道前缀和数组中的第 i 个数是原数组从第1个到第 i 个数的总和,所以设前缀和数组为a,原数组为ori

a[ l ] = ori[ 1 ]+ori[ 2 ]+ori[ 3 ]+…+ori[ l-1 ]+ori[ l ]
a[ r ] = ori[ 1 ]+ori[ 2 ]+ori[ 3 ]+…+ori[ r-1 ]+ori[ r ]

所以用a[ r ]减去a[ l-1 ]即得 ori[ l ]+ori[ l+1 ]+…+ori[ r-1 ]+ori[ r ],即区间[ l , r ]之间的总和。

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

知道了前缀和及其原理后,就可以用来水题了ヽ(°◇° )ノ

P5638 光骓者的荣耀

传送门
一道非常经典的模板题
小k需要得到最快从第 1 个城市走到第 i 个城市并且遍历所有城市的时间,相邻两个城市有不同长度的路连接,他有一个可以从第 i 个城市传送到第 i+k 个城市的不花时间的传送门。
不难推断按顺序从第 1 个城市走到最后一个城市是最快的,求出所有路线的总和再用总和减去传送门能传送的最大距离就是最后的答案。
那么求出传送门能传送的最大距离就是这道题的关键,很容易就能想到前缀和的思想,先预处理一个前缀和数组,用第 k 个数减去第 i-1 个数就能得到从第 i 个城市到第 k 个城市的路线总和
遍历一下所有的传送门传送距离就能得出最大的传送门能传送的距离

代码如下

#include<iostream>
#include<algorithm>
#define ll long long
using namespace std;
ll a[1000010];
int main()
{ll n, k, s = 0;scanf("%lld%lld", &n, &k);for (int i = 1; i < n; i++) {scanf("%lld", &a[i]);a[i] = a[i - 1] + a[i];if (i < k) {//如果i小于k则传送门只能传送第一个城市到第k个城市的距离s = max(s, a[i] - a[0]);}else {s = max(s, a[i] - a[i - k]);}}printf("%lld", a[n - 1] - s);//s为最大传送门距离
}

二维前缀和

二维前缀和,顾名思义就是一维前缀和的二维形态,设原二维数组为 ori[ i ][ j ] ,二维前缀和数组为 a[ i ][ j ],如图所示

二维前缀和
蓝色的部分和红色的部分相加,再减去两部分重叠的绿色部分,最后加上右上角的ori[ i ][ j ],即为要求的黑色部分 a[ i ][ j ]
因此求二维前缀和的公式为

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

同理,求矩阵x1,y1到x2,y2之间的矩阵和的公式为

s = a[ x2 ][ y2 ] - a[ x1-1 ][ y2 ] - a[ x2 ][ y1-1 ] + ori[ x1-1 ][ y1-1 ]

接下来上一道非常简单的例题

P2004 领地选择

传送门

题意很简单,求地图上边长为c的价值总和最大的正方形土地。只要我们遍历一遍地图,将地图上每个点的二维前缀和算出,再利用二维前缀和算出所有边长为c的正方形土地的价值总和,比较大小即可快速求出答案

#include<iostream>
#include<algorithm>
using namespace std;int a[1010][1010];
int main()
{int n, m, c, ma, ansx = 0, ansy = 0;scanf("%d%d%d", &n, &m, &c);for (int i = 1; i <= n; i++) {for (int j = 1; j <= m; j++) {scanf("%d", &a[i][j]);a[i][j] = a[i - 1][j] + a[i][j - 1] - a[i - 1][j - 1] + a[i][j];//二维前缀和预处理}}ma = a[0 + c][0 + c] - a[0][0 + c] - a[0 + c][0] + a[0][0];//初始为第一个边长为c的正方形矩阵for (int i = c; i <=n; i++) {for (int j = c; j <=n; j++) {if (ma < a[i][j] - a[i - c][j] - a[i][j - c] + a[i - c][j - c]) {ansx = i, ansy = j;//记录最大值的坐标}ma = max(ma, a[i][j] - a[i - c][j] - a[i][j - c] + a[i - c][j - c]);}}cout << ansx -c+1 << " " << ansy -c+1;//输出结果
}

最大子段和

前缀和在解题中有非常广泛的应用,其中一种就是求一个数组的最大连续子数组,利用前缀和的原理遍历一遍数组即可求出,时间复杂度为O(n)

P1115 最大子段和

传送门

在求最大子段的类似的题中,我们利用的是类似前缀和的动态规划思想

设原数组为a[N],动态规划数组为dp[N]
第一个数为一个有效序列。
如果一个数加上上一个有效序列得到的结果比这个数大,那么该数也属于这个有效序列。
如果一个数加上上一个有效序列得到的结果比这个数小,那么这个数单独成为一个新的有效序列。
最后处理所有有效序列,输出最大的即可

状态方程为

dp[i]=max(dp[i],dp[i-1]+a[i])

附ac代码

#include<iostream>
using namespace std;
int a[200010];
int main()
{int n, ans = -10000;cin >> n;for (int i = 1; i <= n; i++) {cin >> a[i];a[i] = max(a[i], a[i - 1] + a[i]);ans = max(ans, a[i]);}cout << ans;
}

P1719 最大加权矩形

传送门

与上面一维的求最大子串一样,求二维最大加权矩形的原理大同小异。只要我们利用降维的思想将二维矩阵降为一维的数组,就可以利用上面的公式求出最大的子串,进而求出最大的子矩阵和。

最大子矩阵和

我们用两层循环,遍历子矩阵的上区间和下区间,然后再用一层循环遍历区间之内的每一列的和,然后求出最大的连续的列的和,即可得到最后的答案。输入时利用前缀和的方法预处理每一列的矩阵,方便之后计算每一列的和。

状态方程与上面一维的相同

dp[i]=max(dp[i],dp[i-1]+a[i])

	for (int i = 1; i <= n; i++) {for (int j = i; j <= n; j++) {int x = 0;for (int k = 1; k <= n; k++) {x=max(a[j][k] - a[i - 1][k],a[j][k] - a[i - 1][k]+x);ans = max(ans, x);}}}

暴力计算的时间复杂度为 O( n ^ 4 ),而利用dp与前缀和的思想解决这道题时间复杂度只有O( n ^ 3 ),大大缩短了时间,提高了代码效率。

最后快码加编

#include<iostream>
using namespace std;
int a[200][200];
int main()
{int n, ans = -128;cin >> n;for (int i = 1; i <= n; i++) {for (int j = 1; j <= n; j++) {scanf("%d", &a[i][j]);a[i][j] = a[i - 1][j] + a[i][j];//预处理每一列的前缀和}}for (int i = 1; i <= n; i++) {//遍历下区间for (int j = i; j <= n; j++) {//遍历上区间int x = 0;for (int k = 1; k <= n; k++) {x=max(a[j][k] - a[i - 1][k],a[j][k] - a[i - 1][k]+x);//遍历区间内每一列的最大连续子串和ans = max(ans, x);}}}cout << ans << endl;//轻松ac
}

写在最后

前缀和的思想在很多的题目里都能用到,前缀和主要用于预处理各种数组,以达到快速计算数组的某一区间的和的目的,大量优化代码运行时间,以后碰到类似的求子串和、最大子串的题目时,不妨思考利用前缀和,结合贪心、dp的思想,解决求区间最大、最小值的问题

谢谢你看到最后ლ(╹◡╹ლ)


http://chatgpt.dhexx.cn/article/MudRNmji.shtml

相关文章

前缀和(C/C++)

目录 1. 前缀和的定义 2. 一维前缀和 2.1 计算公式 2.2 用途 2.3 小试牛刀 3. 二维前缀和 3.1 用途 1. 前缀和的定义 对于一个给定的数列A&#xff0c;他的前缀和数中 S 中 S[ i ] 表示从第一个元素到第 i 个元素的总和。 如下图&#xff1a;绿色区域的和就是前缀和数组…

前缀和(C++)实现

每日一句&#xff1a;真正让人变好的选择过程都不会很舒服&#xff0c;所以人们明知道什么都不做&#xff0c;会比较轻松&#xff0c;但依旧选择追逐梦想。 前缀和 前言一、一维前缀和1.前缀和是什么&#xff1f;2.暴力做法3.前缀和求区间大小3.1如何构成前缀和的形式&#xff…

差分与前缀和

目录 前言 一、算法原理 二、算法图解 三、算法模板 四、算法应用 前言 天梯赛和省赛快开始了&#xff0c;不想拖后腿&#xff0c;边更边学习吧。 酝酿好久的第一篇博客&#xff0c;缺点不少&#xff0c;多多指教吖。 点赞鼓励一下吧&#xff0c;亲。&#x1f619;&#x1…

算法设计 - 前缀和 差分数列

一维数组前缀和的概念 前缀和的概念很简单&#xff0c;我们用一维数组的前缀和来举例&#xff0c;有如下一维数组&#xff1a; arr [1, 2, 3, 4, 5] 该数组的前缀和数组如下 preSum [1, 3, 6, 10, 15] 关系如下&#xff1a; preSum[0] arr[0]preSum[1] arr[0] arr[1]pre…

python 前缀和总结

前缀和是数据结构与算法中比较重要的知识&#xff0c;前缀和经常可以结合哈希表解决很多有意思的问题。为了方便学习&#xff0c;在这里总结leetcode中出现的前缀和问题。 525. 连续数组 给定一个二进制数组 nums &#xff08;只含有0&#xff0c;1&#xff09;, 找到含有相同…

【c++入门(2)】前缀和

大纲 1.计算前缀和 2.计算字段和 3.后缀和 4.前缀积 5.后缀积 6.例题 1.计算前缀和 基础问题&#xff1a; 思路1&#xff1a; 枚举 cin (); for (int i 1; i < q; i) {cin >> x;int sum 0;for (int j 1; j < x; j){sum a[j];}cout << sum << endl…

前缀和

【前缀和】 什么是前缀和&#xff1f;前缀和是一个数组的某项下标之前(包括此项元素)的所有数组元素的和。 设b[]为前缀和数组&#xff0c;a[]为原数组&#xff0c;根据这句话可以得到前缀和的定义式和递推式&#xff1a; 定义式递推式一维前缀和 二维前缀和 【一维前缀和】…

Java前缀和算法

一.什么是前缀和算法 通俗来讲&#xff0c;前缀和算法就是使用一个新数组来储存原数组中前n-1个元素的和&#xff08;如果新数组的当前元素的下标为n&#xff0c;计算当前元素的值为原数组中从0到n-1下标数组元素的和&#xff09;&#xff0c;可能这样讲起来有点抽象&#xff0…

基础算法——前缀和详解

秋名山码民的主页 &#x1f389;欢迎关注&#x1f50e;点赞&#x1f44d;收藏⭐️留言&#x1f4dd; &#x1f64f;作者水平很有限&#xff0c;如果发现错误&#xff0c;一定要及时告知作者 前言 由于有些读者朋友私聊我&#xff0c;希望出几期基础算法的讲解&#xff0c;kmp&…

前缀和算法

文章目录 前言一、关于前缀和二、一维数组求前缀和1.求段区间前缀和2.例题&#xff1a;AcWing795. 前缀和AC代码 三、二维数组求前缀和1.求S[i,j]2.求&#xff08;x1&#xff0c;y1&#xff09;&#xff0c;&#xff08;x2&#xff0c;y2&#xff09;子矩阵的和3.例题&#xff…

前缀和详解

目录 前缀和概念前缀和代码前缀和例题题目介绍思路分析相关代码 总结 前缀和概念 前缀和&#xff1a;顾名思义&#xff0c;是要求前缀的总和&#xff0c;什么是前缀&#xff0c;对于一个存放数字的数组而言&#xff0c;前缀就是指的数组的前k项&#xff0c;因此对应的前缀和就…

前缀和与差分 图文并茂 超详细整理(全网最通俗易懂)

目录 1、前缀和2、前缀和算法有什么好处&#xff1f;3、二维前缀和4、差分5、一维差分6、二维差分 1、前缀和 前缀和是指某序列的前n项和&#xff0c;可以把它理解为数学上的数列的前n项和&#xff0c;而差分可以看成前缀和的逆运算。合理的使用前缀和与差分&#xff0c;可以将…

算法:解数独

题目内容 编写一个程序&#xff0c;通过已填充的空格来解决数独问题。 一个数独的解法遵循如下规则&#xff1a; 数字 1-9 在每一行只能出现一次。数字 1-9 在每一列只能出现一次。数字 1-9 在每一个以粗实线分隔的 3x3 宫内只能出现一次。 本题满足以下设定&#xff1a; …

算法_回溯_解数独

文章目录 解数独1.解法2.总结python算法 解数独 leetcode链接 1.解法 这道题是一个棋盘问题&#xff0c;而棋盘问题是一个典型的回溯问题。 首先思考普通人&#xff08;这里指没有受过专业训练的人&#xff09;解数独的方法&#xff0c;那就是首先在空白位置假设一个数字&a…

Java编程实战12:解数独

目录 解数独题目示例 1提示 解答解题思路完整代码 解数独 题目 编写一个程序&#xff0c;通过填充空格来解决数独问题。 数独的解法需 遵循如下规则&#xff1a; 数字 1-9 在每一行只能出现一次。数字 1-9 在每一列只能出现一次。数字 1-9 在每一个以粗实线分隔的 3x3 宫内…

使用java解数独

说明 先根据规则解数独, 规则1: 如果备选数字只有一个, 那么就填入这个数字规则2: 如果在3*3单元格中, 或者一行, 或者一列中, 某个备选数字在所有的备选数字中只出现了一次, 那么就填入这个数字. 再暴力破解数独, 依次填入备选数字, 如果不能解开, 换下一个备选数字, 直到数独…

力扣 37. 解数独

一、题目描述 编写一个程序&#xff0c;通过填充空格来解决数独问题。 数独的解法需遵循如下规则&#xff1a; 数字 1-9 在每一行只能出现一次。数字 1-9 在每一列只能出现一次。数字 1-9 在每一个以粗实线分隔的 3x3 宫内只能出现一次。数独部分空格内已填入了数字&#xf…

LeetCode算法 —— 解数独

题目如下所示&#xff1a; 编写一个程序&#xff0c;通过已填充的空格来解决数独问题。 一个数独的解法需遵循如下规则&#xff1a; 数字 1-9 在每一行只能出现一次。 数字 1-9 在每一列只能出现一次。 数字 1-9 在每一个以粗实线分隔的 3x3 宫内只能出现一次。 空白格用 ‘.…

C语言——解数独程序[源码]

用C语言写的解数独的程序。在linux下测试成功运行。 效果如图&#xff1a; 这是带解的数独&#xff0c;需要填写的部分用数字0代替。 这是程序运行后的效果图。看看&#xff0c;数独已经搞定啦&#xff5e;&#xff5e;&#xff5e; 程序源码如下&#xff1a; #include <…

用 Python 解数独(Sudoku)

芬兰数学家因卡拉花费3个月时间设计出的世界上迄今难度最大的数独。数独是 9 横 9 竖共有 81 个格子&#xff0c;同时又分为 9 个九宫格。规则很简单&#xff1a;每个空格填入 1~9 任意一个数字&#xff0c;需要保证每个横排和竖排以及九宫格内无相同数字。 解数独是一个可有可…