我爱学算法之—— 前缀和(上)

我爱学算法之—— 前缀和(上)

一、【模板】前缀和

题目解析

在这里插入图片描述
这道题,给定一个长度为n的数组,和m次询问;

每一次询问给出两个整数lr,让我们求出区间[l , r]中所有数的和,然后输出。

算法思路

这道题暴力解法:

首先是m次查询(m次测试),每一个给定一个lr,让我们求区间[l , r]中所有数的和。

暴力解法就非常简单了,直接遍历区间[l , r],求出区间中所有数的和即可。

暴力解法时间复杂度O(m * n),也就是O(n^2)级别的时间复杂度;

暴力解法会超时,我们这里想一想可不可以对暴力解法进行一些优化:

  1. 首先m次查询,很显然是不能进行优化的。
  2. 我们只能对求区间[l , r]中所有数的和进行优化。

那如何优化呢?

遍历区间[l , r]来求和时间复杂度是O(n),那我们可不可以用O(1)的复杂度来获得区间[l , r]中所有数的和呢?
在这里插入图片描述

通过上图,我们可以发现:我们要求的[l , r]区间的和s就等于区间[1 , r]的和 减去区间[1 , l]的和。

前缀和

所以,我们可以通过运算来用O(1)的时间复杂度获得区间[l , r]中所有数的和;但是我们要用到区间[1 , l]和区间[1 , r]中所有数的和。

所以我们预先既要处理一个前缀和数组dp:其中dp[i]:表示区间[1 , i]中所有数的和。填写前缀和数组:dp[i] = dp[i-1] + arr[i](也就是前面所有数的和加上当前位置的数)。计算区间[l , r]中所有数的和:dp[r] - dp[l-1](这里区间[l , r]包含l位置,所以要减去dp[i-1]
这里可以说:前缀和和动态规划的大致思路非常相似:

状态表示dp[i]表示区间[1 , i]中所有数的和

状态转移方程dp[i] = dp[i] + arr[i];

获取区间[l , r]中所有数的和s = dp[r] - dp[l-1];

代码实现

#include<cmath>#include<iostream>usingnamespace std;constint N =100001;longlong dp[N];int arr[N];int n, m;intmain(){ cin >> n >> m;for(int i =1; i <= n; i++){ cin >> arr[i]; dp[i]= dp[i -1]+ arr[i];}while(m--){int l, r; cin >> l >> r; cout << dp[r]- dp[l -1]<< endl;}return0;}

二、【模板】二维前缀和

题目解析

在这里插入图片描述
对于这道题,给定一个n*m的二维数组,以及q次查询;

每一次查询给定x1,x2,y1,y2,我们要求以(x1,y1)为左上角,(x2,y2)为右下角的子矩阵中所有数的和。

算法思路

暴力解法:

q次查询,每一查询给定x1,y1,x2,y2,遍历整个子矩阵进行求和操作。

时间复杂度:O(n*m*q),也就是O(n^3)级别的时间复杂度。

很显然会超时,对暴力解法进行优化,很显然只能优化求子矩阵中所有元素的和。

暴力解法中,遍历整个子矩阵去求和,这样太麻烦了;我们可不可以使用O(1)的时间复杂度拿到子矩阵中所有数的和?

当然也是可以的,这就像数学当中求一块面积的和一样。
在这里插入图片描述

如上图所示,我们要求以(x1 , y1)为左上角,(x2 , y2)为右下角的子矩阵中所有数的和,也就是S

我们只要知道s1(以(1 , 1)为左上角,(x1-1 , y1)为右下角的子矩阵的和)、s2(以(1 , 1)为左上角,(x2, y1-1)为右下角的子矩阵的和)、s3(以(1 , 1)为左上角,(x1-1 , y1-1)为右下角的子矩阵的和)以及s4(以(1 , 1)为左上角,(x2 , y2)为右下角的子矩阵的和)。

我们就可以通过数学运算来求Ss = s4 - s1 - s2 + s3

也就是s = dp[x2][y2] - dp[x2][y1-1] - dp[x1-1][y2] + dp[x1-1][y1-1]

这样我们在填写前缀和表时:

在这里插入图片描述

dp[i][j] = dp[i][j-1] +dp[i-1][j] - dp[i-1][j-1] + arr[i][j]

这里也可以将前缀和理解为动态规划

状态表示dp[i][j]表示以(1,1)为左上角,(i,j)为右下角的子矩阵中所有数的和。

状态转移方程dp[i][j] = dp[i][j-1] +dp[i-1][j] - dp[i-1][j-1] + arr[i][j]

计算子矩阵中所有数的和s = dp[x2][y2] - dp[x2][y1-1] - dp[x1-1][y2] + dp[x1-1][y1-1]

代码实现

#include<iostream>usingnamespace std;constint N =1001;int arr[N][N];longlong dp[N][N];int n, m, q;int x1, x2, y1, y2;intmain(){ cin >> n >> m >> q;for(int i =1; i <= n; i++){for(int j =1; j <= m; j++){ cin >> arr[i][j]; dp[i][j]= dp[i -1][j]+ dp[i][j -1]- dp[i -1][j -1]+ arr[i][j];}}while(q--){ cin >> x1 >> y1 >> x2 >> y2; cout <<(dp[x2][y2]- dp[x1 -1][y2]- dp[x2][y1 -1]+ dp[x1 -1][y1 -1])<< endl;}return0;}

总结

这里简单总结一下前缀和算法:

首先前缀和算法可以用来快速的求出子数组/子矩阵中所有数的和,在涉及到求子数组/子矩阵的和时,能够利用前缀和算法来快速的求和。

其次,使用前缀和,我们就要预先构建一个前缀和数组并填写该数组;(和动态规划类似)

注意:在构建前缀和数组时,通常下标从1开始,因为在填写数组时要用到dp[i-1]

最后,前缀和算法就是空间换时间,通过预先构建前缀和数组,让我们能够在O(1)的数据复杂度拿到子数组/子矩阵的和。

到这里本篇文章内容就结束了,感谢各位大佬的支持

Read more

PDFShuffler:简单高效的PDF页面管理工具终极指南

PDFShuffler:简单高效的PDF页面管理工具终极指南 【免费下载链接】pdfarranger 项目地址: https://gitcode.com/gh_mirrors/pdf/pdfshuffler PDFShuffler是一款开源免费的PDF页面管理软件,专为需要重新排列、合并、拆分PDF文件的用户设计。无论您是学生整理讲义、商务人士整合报告,还是普通用户处理日常文档,PDFShuffler都能让PDF操作变得轻松简单! 为什么选择PDFShuffler? 🎯 完全免费开源 PDFShuffler基于MIT许可证开源,这意味着您可以免费使用所有功能,无需担心版权问题。开源社区持续维护,确保软件稳定性和功能更新。 🚀 跨平台兼容 基于Python和PyQt开发,PDFShuffler完美支持Linux、Windows和Mac OS三大主流操作系统,让您在不同设备上都能享受一致的PDF编辑体验。 👆 直观易用的界面 从截图中可以看到,PDFShuffler的界面设计非常直观: * 网格视图:以缩略图形式清晰展示所有PDF页面 * 拖拽排序:只需

By Ne0inhk
直流无刷电机FOC控制算法

直流无刷电机FOC控制算法

文章目录 * 1、FOC概述 * 1.1 FOC控制算法介绍 * 2、无刷电机 * 2.1 无刷电机介绍 * 2.2 无刷电机和永磁同步电机的区别 * 2.3 无刷电机的控制原理 * 2.3.1 无刷电机工作原理 * 2.3.2 直流无刷电机驱动原理 * 2.3.2.1 有感直流无刷电机六步换相驱动原理 * 2.3.2.2 直流无刷电机FOC控制原理 * 3、无刷电机FOC控制算法 * 3.1 FOC控制算法整体流程 * 3.2 FOC算法Clarke变换 * 3.2.1 Clarke变换公式推导 * 3.2.2

By Ne0inhk
《算法题讲解指南:递归,搜索与回溯算法--递归》--3.反转链表,4.两两交换链表中的节点,5.快速幂

《算法题讲解指南:递归,搜索与回溯算法--递归》--3.反转链表,4.两两交换链表中的节点,5.快速幂

🔥小叶-duck:个人主页 ❄️个人专栏:《Data-Structure-Learning》 《C++入门到进阶&自我学习过程记录》 《算法题讲解指南》--优选算法 《算法题讲解指南》--递归、搜索与回溯算法 ✨未择之路,不须回头 已择之路,纵是荆棘遍野,亦作花海遨游 目录 3.反转链表 题目链接: 题目描述: 题目示例: 解法(递归): 算法思路: C++算法代码: 算法总结及流程解析: 4.两两交换链表中的节点 题目链接: 题目描述: 题目示例: 解法(递归): 算法思路: C++算法代码: 算法总结及流程解析: 5.快速幂 题目链接: 题目描述: 题目示例: 解法(递归-快速幂): 算法思路: C+

By Ne0inhk