算法专题四: 前缀和

目录

  • 1. 前缀和
  • 2. 二维前缀和
  • 3. 寻找数组的中心下标
  • 4. 除自身以外数组的乘积
  • 5. 和为k的子数组
  • 6. 和可被K整除的子数组
  • 7. 连续数组
  • 8. 矩阵区域和

博客主页:酷酷学!!!
感谢关注~


1. 前缀和

在这里插入图片描述

算法思路:

根据题意, 创建一个前缀和数组, dp[i] = dp[i -1] + arr[i], 再使用前缀和数组, 要求的区域ret = dp[r] - dp[l-1], 这里我们为什么要这样求dp[i]呢? 还要绕一大圈子, 直接相加不就行了 , 但是如果直接相加求还不如我们的暴力解法呢, 这里还要开辟空间, 但是我们使用dp[i]求解只需遍历一遍数组即可求出前缀和

需要注意:

  1. 创建前缀和数组默认从0开始, 如果我们从1开始访问则需要先将数组初始化好,
  2. 因为dp[i] 的大小可能超过int所以需要创建long long类型的数组.

编写代码:

#include <iostream>
#include <vector>
using namespace std;int main() 
{int n = 0, q = 0;cin>>n>>q;vector<long long> dp(n+1,0);int num = 0;for(int i = 1; i <= n; i++){cin>>num;dp[i] = dp[i-1] + num;}while(q--){int l,r;cin>>l>>r;cout<<dp[r] - dp[l-1]<<endl;}return 0;
}
// 64 位输出请用 printf("%lld")

2. 二维前缀和

在这里插入图片描述
算法思路:

在这里插入图片描述

在这里插入图片描述

分析题意, 很显然如果我们使用暴力解法一定会超时的.
首先分析题目, 我们可以先预处理出来一个前缀和矩阵如下图所示, 求出每一个dp[i][j]
在这里插入图片描述
然后我们使用dp[i][j],根据所求的区域我们就可以找出一个求出结果的公式, 于是我们就可以搞出来一个时间复杂度为O(N)的算法,当然空间复杂度也为O(N).

在这里插入图片描述
编写代码:

#include<iostream>
#include<vector>
using namespace std;
int main()
{   int n,m,q;cin>>n>>m>>q;vector<vector<int>> arr(n+1,vector<int>(m+1));for(int i = 1; i <= n; i++){for(int j = 1; j <= m; j++){cin>>arr[i][j];}}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] - dp[i-1][j-1] + arr[i][j];}}int x1,y1,x2,y2;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;}return 0;
}

3. 寻找数组的中心下标

在这里插入图片描述

算法思路1:

分析题意, 发现可以使用前缀和来解决, 首先预处理出来一个前缀和数组, 然后我们只需在遍历一遍前缀和数组, 只要找到一个位置前面的区域和后面的区域相同, 则就找到了该位置, 但是注意我们的dp是从1开始的所以返回结果需要-1, 如果没有找到返回-1即可, 注意预处理前缀和数组的时候与原数组的映射关系.

编写代码:

class Solution {
public:int pivotIndex(vector<int>& nums) {int n = nums.size();vector<int> dp(n+1,0);for(int i = 1; i <= n ;i++){dp[i] = dp[i-1] + nums[i-1];}for(int i = 1; i <= n ;i++){if(dp[i-1] == (dp[n] - dp[i]))return i-1;}return -1;}
};

算法思路二:

既然我要算左边和右边的和是否相等, 那么我们不妨弄两个的dp数组, 一个前缀和一个后缀和, 对于前缀和我们只需要求出i之前的所有元素之和即可, 对于后缀和我们只需要求出i位置之后的所有元素之和即可, 但是注意细节我们需要考虑第一次计算会访问越界的情况, 所以我们需要提前把f[0]和g[n-1]这个位置处理好,而vector默认就为0.
在这里插入图片描述

在这里插入图片描述

编写代码:

class Solution {
public:int pivotIndex(vector<int>& nums) {int n = nums.size();vector<int> f(n),g(n);for(int i = 1 ; i < n; i++){f[i] = f[i-1] + nums[i-1];}for(int i = n - 2; i >= 0; i--){g[i] = g[i+1] + nums[i+1];}for(int i = 0; i < n ; i++){if(g[i] == f[i]) return i;}return -1;}
};

4. 除自身以外数组的乘积

在这里插入图片描述
算法思路:

有了上一题思路二的方法, 对于这道题我们不难解决, 只需求出对于i之前的前缀积以及对于i之后的后缀积即可, 遍历i位置此时answer[i]的位置就是f[i]*g[i], 但是注意细节,对于f[0]和g[n-1]我们要处理成1, 默认第一个位置之前的积为1,最后一个位置也是.

在这里插入图片描述

编写代码:

class Solution {
public:vector<int> productExceptSelf(vector<int>& nums) {int n = nums.size();vector<int> f(n),g(n),ret(n);f[0] = g[n-1] = 1;for(int i = 1 ; i < n; i++)f[i] = f[i-1] * nums[i-1];for(int i = n-2; i >= 0; i--)g[i] = g[i+1] * nums[i+1];for(int i = 0; i < n;i++)ret[i] = g[i] * f[i];return ret;}
};

5. 和为k的子数组

在这里插入图片描述

算法思路:

首先我们肯定会想到暴力求解, 但是时间复杂度为O(N^2), 那可不可以使用双指针呢, 也就是我们的滑动窗口, 也不可以, 因为会有负数, 并没有单调性.

在这里插入图片描述

对于一个数组, 我们要求这个数组中和为k的子数组, 比如遍历到i位置, 我们要求i往前的和为k的子数组, 仅需从0位置查找, 找到和为sum[i] - k的子数组即可, 所以我们的前缀和思想又可以派上用场了.

首先我们先计算出前缀和, 但是我们并不需要创建一个前缀和数组, 仅需把每一次的结果记录到sum即可, 遍历一遍数组, 遍历到i位置就找i位置之前有没有sum = nums[i] - k的, 我们可以把每一次的结果放到哈希表中, 但是注意我们并不需要i这个位置的和, 而是i之前的, 所以我们只保存i之前的前缀和即可, 如果有则ret++,继续下一次查找, 但是注意如果整个数组等于k,也就是遍历到第一个位置我们需要在0到-1这个区间查找, 所以我们需要处理hash[0] = 1.

在这里插入图片描述

编写代码:

class Solution {
public:int subarraySum(vector<int>& nums, int k) {unordered_map<int,int> hash;int sum = 0, ret = 0;hash[0] = 1;for(int i = 0; i < nums.size();i++){sum += nums[i];if(hash.count(sum - k)) ret += hash[sum - k]; hash[sum]++;}return ret;}
};

6. 和可被K整除的子数组

在这里插入图片描述

算法思路:

本道题依然不能使用滑动窗口来解决, 更上一道题思路类似, 只不过我们这道题哈希表里面存放的是sum的余数, 当遍历到i位置的时候只需要判断在i位置之前和除以k的余数是否等于sum除以k的余数, 因为同余定理, 可以看下图

在这里插入图片描述
也就是我们仅需在i位置前找到x%k等于sum%即可, 所以判断的时候我们需要判断sum的余数, 所以哈希表里面我们需要存放余数, 但是C++中余数是由负数的, 负数和正数取余结果不一样, 所以我们需要进行修正. 使用sum遍历数组, 并且求出sum的余数r, 然后先判断sum之前的余数是否等于r, 如果等于则更新ret,最后将这个r也丢入到哈希表中.

在这里插入图片描述

在这里插入图片描述

编写代码:

class Solution {
public:int subarraysDivByK(vector<int>& nums, int k) {unordered_map<int,int> hash;hash[0 % k] = 1;int sum = 0, ret = 0;for(int i = 0; i < nums.size() ; i++){sum += nums[i];int r = (sum % k + k) % k;if(hash.count(r)) ret += hash[r];hash[r]++;}return ret;}
};

7. 连续数组

在这里插入图片描述

算法原理:

这一道题没有单调性, 我们还是不能使用滑动窗口, 先把0修改成-1,我们就可以发现规律, i位置和在[0,i-1]位置的值相等, 即就能找到k数组, 因为正负抵消, 遍历到i位置我们判断该位置之前是否有和正好等于sum, 如果有则我们更新结果, 这里注意我们哈希表中需要存储下标, 因为结果需要我们求出长度, 如果有重复的sum, 我们只保存最早出现的那个下标.
在这里插入图片描述

class Solution {
public:int findMaxLength(vector<int>& nums) {unordered_map<int,int> hash;hash[0] = -1;int sum = 0 ,ret = 0;for(int i = 0; i < nums.size(); i++){sum += nums[i] == 0 ? -1 : 1;if(hash.count(sum)) ret = max(ret,i - hash[sum]);else hash[sum] = i;}return ret;}
};

8. 矩阵区域和

在这里插入图片描述

算法思路:

首先读懂题意, 如下图所示, 也就是answer数组中的每一个位置就是mat当前位置的值向周围拓展k个单位元素的和.接下来进行分析

在这里插入图片描述

这道题要求我们求出拓展k个单位的和, 我们应该联想到一个算法, 二维前缀和dp.回忆一下二维前缀和的用法.如果说我们要求一个矩阵的前缀和数组,
则dp[ i ] [ j ] = dp[ i - 1 ] [ j ] + dp[ i ] [ j-1 ] - dp[ i - 1] [j - 1] + arr[ i ] [ j ]

在这里插入图片描述
对于使用则有, 如果我们要求某一段区域的和,
ret = dp[x2][y2] - dp[x1-1][y2] - dp[x2][y1-1] + dp[x1-1][y1-1]

在这里插入图片描述
可以看出, 我们只要想求某段区域的和则前缀和数组中, 仅需知道这块区域的左上角下标个右下角下标即可,本题我们也可以用这样的思路, 但是有几个细节需要注意:

1, 注意我们创建dp数组时, 为了避免越界访问一般下标从1开始, 但是本题所给的数组mat是从下标0 开始, 所以我们它的dp数组需要稍作修改, 当时用到mat时, 将下标+1处理

在这里插入图片描述

然后创建我们的ret数组, ret数组中的每一个位置我们都需要使用到dp数组, 所以需要求每一个位置的左上角位置与右下角位置的下标, 以便于正确的使用dp数组.根据下面我们求出该区域与使用dp数组的映射关系, 然后对于每一个位置分别求其结果.

在这里插入图片描述
编写代码:

class Solution {
public:vector<vector<int>> matrixBlockSum(vector<vector<int>>& mat, int k) {int m = mat.size(), n = mat[0].size();vector<vector<int>> dp(m + 1,vector<int>(n + 1));for(int i = 1; i <= m; i++){for(int j = 1; j <= n; j++){dp[i][j] = dp[i-1][j] + dp[i][j-1] - dp[i-1][j-1] + mat[i-1][j-1];}}vector<vector<int>> ret(m,vector<int>(n));for(int i = 0; i< m;i++){for(int j = 0;j < n; j++){int x1 = max(0,i-k) + 1, y1 = max(0,j-k) + 1;int x2 = min(m-1,i+k) + 1, y2 = min(n-1,j+k) + 1;ret[i][j] = dp[x2][y2] - dp[x1 - 1][y2] - dp[x2][y1-1] +dp[x1-1][y1-1];}}return ret;}
};

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

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

相关文章

排查和解决JVM OOM实战

JVM OOM介绍 Java内存区域布局 下面的分析中都是基于JDK 8开始的。关于JMM不过多介绍每个区域的作用。OOM不单只会发生在堆内存&#xff0c;也可能是因为元空间或直接内存泄漏导致OOM&#xff0c;此时在OOM的详细信息中会有不同体现。 Java OOM的类别 java.lang.OutOfMemory…

王者农药更新版

一、启动文件配置 二、GPIO使用 2.1基本步骤 1.配置GPIO&#xff0c;所以RCC开启APB2时钟 2.GPIO初始化&#xff08;结构体&#xff09; 3.给GPIO引脚设置高/低电平&#xff08;WriteBit&#xff09; 2.2Led循环点亮&#xff08;GPIO输出&#xff09; 1.RCC开启APB2时钟。…

HarmonyOS/OpenHarmony 自定义弹窗页面级层级控制解决方案

关键词&#xff1a;CuntomDialog自定义弹窗、SubWindow子窗口、页面级、弹窗层级控制、鸿蒙、弹窗展示层级异常 问题存在API版本&#xff1a;API10 - API12&#xff08;该问题已反馈&#xff0c;期望后续官方能增加页面级控制能力&#xff09; 在正常的鸿蒙app开发过程中&…

TIM(Timer)定时器的原理

一、介绍 硬件定时器的工作原理基于时钟信号源提供稳定的时钟信号作为计时器的基准。计数器从预设值开始计数&#xff0c;每当时钟信号到达时计数器递增。当计数器达到预设值时&#xff0c;定时器会触发一个中断信号通知中断控制器处理相应的中断服务程序。在中断服务程序中&a…

LeetCode讲解篇之239. 滑动窗口最大值

文章目录 题目描述题解思路题解代码题目链接 题目描述 题解思路 我们维护一个长度为k的窗口&#xff0c;然后窗口从数组最左边一直移动到最右边&#xff0c;记录过程中窗口中的最大值&#xff0c;就是答案 我们每次查询长度为k的窗口最大值是什么时间复杂度是O(k)的&#xff0…

rust中async/await的使用

在Rust中,async/await 用于编写异步代码。它允许您以同步的方式编写异步代码,使得异步操作更易于理解和编写。 安装依赖: cargo add futures cargo add async-std 使用示例: 示例1: use async_std::task::block_on;fn main() {block_on(hello()); }async fn hello() …

MoveIt2-humble----Planning Around Objects

1 添加Planning Scene Interface头文件 #include <moveit/planning_scene_interface/planning_scene_interface.h>2 改变目标位姿 // Set a target Pose auto const target_pose [] {geometry_msgs::msg::Pose msg;msg.orientation.w 1.0;msg.position.x 0.28;msg.p…

Github 2024-10-06 php开源项目日报 Top10

根据Github Trendings的统计,今日(2024-10-06统计)共有10个项目上榜。根据开发语言中项目的数量,汇总情况如下: 开发语言项目数量PHP项目10Blade项目2Laravel:表达力和优雅的 Web 应用程序框架 创建周期:4631 天开发语言:PHP, BladeStar数量:75969 个Fork数量:24281 次…

Linux安装部署MySQL8.0加遇着问题解决

1.首先我先给个URL下载MySQL官方网站https://downloads.mysql.com/archives/community/ 2.选择Linux的红帽系统 3.接着选择红帽系统的7版本,x86 4.接着选择MySQL版本,此时我选择8.4.0,下载rpm bundle这个,下载下面这个就好 5.Windows文件上传到Linux系统 rz上传文件命令,找到…

【unity游戏开发】彻底理解AnimatorStateInfo,获取真实动画长度

前言 前置知识&#xff1a;设置参数后&#xff0c;下一个循环才会切换对应动画&#xff0c;所以在下一个循环获取真实的动画长度 AnimatorStateInfo是结构体&#xff01;值类型&#xff0c;要不断重复获取才是最新的 主要是自动设置trigger切换的动画自动切回上一个动画&#x…

Java中如何实现定时任务?

目录 一、定时任务 概念 作用 二、简单定时任务实现方式 1. Thread线程等待&#xff08;最原始最简单方式&#xff09; 2. 使用java.util.Timer Timer 优缺点分析 3. 使用JDK自带的ScheduledExecutorService schedule和scheduleAtFixedRate的区别 schedule侧重保持间隔…

B 私域模式升级:开源技术助力传统经销体系转型

一、引言 1.1 研究背景 随着市场竞争加剧&#xff0c;传统经销代理体系面临挑战。同时&#xff0c;开源技术发展迅速&#xff0c;为 B 私域升级带来新机遇。在当今数字化时代&#xff0c;企业面临着日益激烈的市场竞争。传统的经销代理体系由于管理效率低下、渠道局限、库存压…

贝锐蒲公英网盘首发,秒建私有云,高速远程访问

虽然公共网盘带来了不少便利&#xff0c;但是大家对隐私泄露和重要数据泄密的担忧也随之增加。如果想要确保数据安全&#xff0c;自建私有云似乎是一条出路&#xff0c;然而面对搭建私有云的复杂步骤&#xff0c;许多人感到力不从心&#xff0c;NAS设备的成本也往往让人望而却步…

项目——超级马里奥——Day(2)

争取今天晚上能搞一半啊&#xff0c;啊啊啊啊&#xff0c;感觉事多的忙不过来 设计思路&#xff1a; 1&#xff09;创建并完成常量类 ------->一张图片的情况 先完成对图片的封装------>把图片加载一遍 &#xff08;老实说&#xff0c;我也不太知道为什么&#xff0…

【项目安全设计】软件系统安全设计规范和标准(doc原件)

1.1安全建设原则 1.2 安全管理体系 1.3 安全管理规范 1.4 数据安全保障措施 1.4.1 数据库安全保障 1.4.2 操作系统安全保障 1.4.3 病毒防治 1.5安全保障措施 1.5.1实名认证保障 1.5.2 接口安全保障 1.5.3 加密传输保障 1.5.4终端安全保障 资料获取&#xff1a;私信或者进主页。…

约数个数约数之和

好久没发文章了.......不过粉丝还是一个没少...... 今天来看两道超级恶心的数论题目&#xff01; No.1 约数个数 No.2 约数之和 先来看第一道&#xff1a;约数个数 题目描述 给定 n 个正整数 ai​,请你输出这些数的乘积的约数个数,答案对 10^97 取模 输入格式 第一行包含…

OBOO鸥柏丨OLED透明触摸查询一体机数字科技触控广告屏技术前沿

吊挂透明OLED触摸屏一体机正成为博物馆数字化展示的“共同奔赴赛道选择&#xff0c;透过透明屏幕看到展示物品的内部结构和细节&#xff0c;GG纯平面触控实现展示查询交互与互动的完美结合。相比传统的商用/工业液晶显示屏机柜&#xff0c;OLED透明触摸屏具有更高的对比度和更广…

佳易王电玩店ps5计时计费系统软件倒计时语音提醒软件操作教程

一、前言 【试用版软件下载可以点击最下方官网卡片】 佳易王电玩店ps5计时计费系统软件倒计时语音提醒软件操作教程 1、时间显示&#xff1a;正常使用时间&#xff0c;直观显示在对应桌旁。 2、倒计时显示&#xff1a;右侧显示剩余多少分钟&#xff0c; 3、定时语音提醒&am…

阿里云域名注册购买和备案

文章目录 1、阿里云首页搜索 域名注册2、点击 控制台3、域名控制台 1、阿里云首页搜索 域名注册 2、点击 控制台 3、域名控制台

YOLOv11改进,YOLOv11添加DCNv4可变性卷积(windows系统成功编译),二次创新C2f结构,全网最详细教程

改进训练结果前: 二次创新C2f结构训练结果: 摘要 引入了可变形卷积 v4 (DCNv4),这是一种为广泛视觉应用设计的高效且有效的操作算子。DCNv4通过两项关键增强解决了其前身DCNv3的局限性:1. 移除空间聚合中的softmax归一化,以增强其动态特性和表达能力;2. 优化内存访问以…