算法:图解前缀和问题

news2024/11/18 4:52:06

文章目录

  • 实现原理
  • 实现思路
    • 一维前缀和模板
    • 二维前缀和模板
  • 典型例题
    • 一维前缀和
    • 二维前缀和
    • 寻找数组中心下标
    • 除自身以外数组的乘积
    • 关系矩阵和
  • 总结

实现原理

前缀和问题和二分查找类似,也是有一些固定的模板的,在理解原理的基础上进行实践,就能解决大多数问题

前缀和问题的题目问法通常是计算某个序列中子序列的之和,如果采用暴力求解的话就是遍历一遍数组,假设要找M个子序列,序列长度总共为N,那么时间复杂度就是O(M*N),而前缀和算法原理可以优化这个时间复杂度

实现原理有些类似于动态规划,原理就是预处理一个前缀和数组,然后利用前缀和数组解决问题即可

下面介绍算法的实现思路

实现思路

在这里插入图片描述

现在我们有数组arr,要求的结果是取出qarr中的子序列,计算这些子序列的长度,算法原理开辟一个和arr一样大的数组,作为dp数组,数组内存储的是对应arr中对应下标前所有位置数之和

一维前缀和模板

vector<long long> v(n+1);
for (int i = 1; i <= n; i++)
{
    cin>>v[i];
}
vector<long long> dp(n+1);
dp[0]=0;
for (int i = 1; i <= n; i++)
{
    dp[i]= dp[i - 1] + v[i];
}

自此,一维前缀和总体结束,下面是二维前缀和的题目解析

在这里插入图片描述

整体来看,二维前缀和和一维前缀和相差不算很大,代码实现的思路也很好想,可以图解如下表示

假设现在有下图所示的二维序列,这里面存储的是一些数据

在这里插入图片描述

依据题意,假设这里需要求4,3的内容,因此需要计算的是这片区域的面积

在这里插入图片描述
因此,我们可以仿照前面的算法,也用dp原理创建一个dp数组,数组中每一个元素存储的是从1,1到该位置这块区域内的所有元素的和

那么区间内数组如何计算?如果采用一个一个遍历找的时间复杂度显然是不合适的,因此需要找到一种更高效的复杂度方式,可以采用如下方式

在这里插入图片描述

对于我们要求的彩色区域,可以划分为四个子区域进行求解,那么面积如何求?如果采用A+B+C+D直接求,是不合理的,原因在于B和C的面积不能直观的求解,因此可以改成(A+C)+(A+B)+D-A,通过这样划分区域,就可以求解问题

抽象化上面的思路,假设要求i,j位置的值,就可以抽象化为dp[i][j]=dp[i-1][j]+dp[i][j-1]+arr[i][j]-dp[i-1][j-1]

那现在根据上面的算法原理,已经把dp数组求出来了,那么后续如何求解?

假设现在要求的是2,2,4,4,理论上所求区域如下在这里插入图片描述
那么对应到dp数组就是下面的区域

在这里插入图片描述

将整个区域进行抽象化演示,即看成从m,np,q

因此这个区域面积就可以表示为dp[m][n]-dp[p-1][q]-dp[p][q-1]+dp[p-1][q-1]

因此,二维前缀和的做题思路就抽象化出来了,首先用dp数组表示出一个和数组,再利用和数组面积的加减关系求解出最终答案即可

二维前缀和模板

// 数据输入
for (int i = 1; i <= m; i++)
{
    for (int j = 1; j <= n; j++)
    {
        cin >> v[i][j];
    }
}
// 处理dp数组
vector<vector<long long>> dp(m + 1, vector<long long>(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] + v[i][j] - dp[i - 1][j - 1];
    }
}

典型例题

一维前缀和

在这里插入图片描述

#include <bits/stdc++.h>
using namespace std;

int main()
{
    int n, q;
    cin >> n >> q;
    vector<long long> v(n+1);
    for (int i = 1; i <= n; i++)
    {
        cin>>v[i];
    }
    vector<long long> dp(n+1);
    dp[0]=0;
    for (int i = 1; i <= n; i++)
    {
        dp[i]= dp[i - 1] + v[i];
    }
    while (q--)
    {
        int l, r;
        cin >> l >> r;
        cout << dp[r] - dp[l - 1] << endl;
    }
    return 0;
}

二维前缀和

在这里插入图片描述

#include <iostream>
#include <vector>
using namespace std;

int main()
{
    int m = 3, n = 4, q = 3;
    cin >> m >> n >> q;
    vector<vector<int>> v(m + 1,vector<int>(n+1));
    // 数据输入
    for (int i = 1; i <= m; i++)
    {
        for (int j = 1; j <= n; j++)
        {
            cin >> v[i][j];
        }
    }
    // 处理dp数组
    vector<vector<long long>> dp(m + 1, vector<long long>(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] + v[i][j] - dp[i - 1][j - 1];
        }
    }
    // 处理数据
    while (q--)
    {
        int x1, y1, x2, y2;
        cin >> x1 >> y1 >> x2 >> y2;
        cout << dp[x2][y2] - dp[x2][y1-1] - dp[x1-1][y2] + dp[x1 - 1][y1 - 1] << endl;
    }
    return 0;
}

寻找数组中心下标

在这里插入图片描述

本题应用了前缀和的思路,写入两个表,一个从后向前写,一个从前向后写,其中要注意对于这个题的边界下标问题,尤其是从右向左写容易越界

处理前缀和问题要想清楚,前后缀和数组的含义是当前下标之前/后元素,不含当前下标元素

class Solution 
{
public:
    int pivotIndex(vector<int>& nums) 
    {
        int n=nums.size();
        // 处理前缀和
        vector<int> f(n);
        f[0]=0;
        for(int i=1;i<n;i++)
        {
            f[i]=f[i-1]+nums[i-1];
        }

        // 处理后缀和
        vector<int> g(n);
        g[n-1]=0;
        for(int i=n-2;i>=0;i--)
        {
            g[i]=g[i+1]+nums[i+1];
        }

        // 处理数据
        for(int i=0;i<n;i++)
        {
            if(f[i]==g[i])
            {
                return i;
            }
        }
        return -1;
    }
};

除自身以外数组的乘积

在这里插入图片描述

此题也是很明显的前缀和问题,在解题的时候,要理清思路,一要处理好边界问题,二要想清楚原理,边界问题提前处理

class Solution 
{
public:
    vector<int> productExceptSelf(vector<int>& nums)
    {
        int n = nums.size();
        // 前缀和数组
        vector<int> f(n);
        f[0] = 1;
        for (int i = 1; i < n; i++)
        {
            f[i] = f[i - 1] * nums[i - 1];
        }

        // 后缀和数组
        vector<int> g(n);
        g[n - 1] = 1;
        for (int i = n - 2; i >= 0; i--)
        {
            g[i] = g[i + 1] * nums[i + 1];
        }

        // 返回数组
        vector<int> res(n);
        res[0] = g[0];
        res[n - 1] = f[n - 1];
        for (int i = 1; i < n - 1; i++)
        {
            res[i] = f[i] * g[i];
        }
        return res;
    }
};

关系矩阵和

在这里插入图片描述

二维数组的前缀和问题中的简单应用,其中关于二维数组前缀和主要就是要注意dp数组的写入,在这个过程中涉及到的边界问题,其余如果理解原理应用模板是可以很好解决的

class Solution 
{
public:
    vector<vector<int>> matrixBlockSum(vector<vector<int>>& mat, int k) 
    {
        int m=mat.size();
        int 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>> ans(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,i+k+1),y2=min(n,j+k+1);
                ans[i][j]=dp[x2][y2]-dp[x2][y1-1]-dp[x1-1][y2]+dp[x1-1][y1-1];
            }
        }
        return ans;
    }
};

总结

前缀和问题也是较为重要的算法,重点就是要理解dp数组的意义,尤其是对于二维数组的前缀和,要理解才能正确写出

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

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

相关文章

使用小程序实现左侧菜单,右侧列表双向联动效果

目录 引言理解双向联动效果的重要性scrollview属性介绍实现左侧菜单数据准备渲染菜单列表监听菜单点击事件实现右侧列表数据结构设计初始数据渲染监听列表滚动事件左侧菜单与右侧列表联动获取当前滚动位置计算对应菜单项联动效果优化用户体验考虑平滑滚动效果菜单高亮状态

VMware 修改ip地址 虚拟机静态ip设置 centos动态ip修改为静态ip地址 centos静态ip地址 vmware修改ip地址

虚拟机的centos服务器经常变换ip&#xff0c;测试起来有些麻烦&#xff0c;故将动态ip修改为静态ip 1. 查看vmware 虚拟机网络配置&#xff1a; 点击编辑&#xff0c;打开虚拟网络配置 2. 选中nat模式&#xff0c;点击nat设置&#xff0c;最终获取网关ip: 192.168.164.2 3. 进…

【ES5】—ES5中类与继承

一、如何定义类 类的首字母要大写&#xff0c;用于识别出与普通函数的不同 // 类 构造函数 function People () {this.name xiaoxiaothis.age 18 } // 实例化对象 let p1 new People()二、动态属性和方法 1. 动态属性再构造函数里面定义 // 类 function People (name, …

<C++> 类和对象(中)-类的默认成员函数

1.类的默认成员函数 默认成员函数&#xff1a;用户没有显式实现&#xff0c;编译器会生成的成员函数称为默认成员函数。 如果一个类中什么成员都没有&#xff0c;简称为空类。 空类中真的什么都没有吗&#xff1f;并不是&#xff0c;任何类在什么都不写时&#xff0c;编译器会…

【深度学习_TensorFlow】过拟合

写在前面 过拟合与欠拟合 欠拟合&#xff1a; 是指在模型学习能力较弱&#xff0c;而数据复杂度较高的情况下&#xff0c;模型无法学习到数据集中的“一般规律”&#xff0c;因而导致泛化能力弱。此时&#xff0c;算法在训练集上表现一般&#xff0c;但在测试集上表现较差&…

【Vue3】Vuex状态管理

Vuex状态管理 附&#xff1a;eslin中定义了不使用会报错认识应用状态管理什么是状态管理旧模式下的状态管理 Vuex的基本使用安装Vuex单一状态树 State使用vuex的方式&#xff08;Vue3&#xff09;mapState辅助函数&#xff08;Vue2&#xff09; Mutationsmutations的使用和携带…

前向传播与反向传播涉及到的知识点

目录 引入&#xff1a; 一、神经网络&#xff08;NN&#xff09;复杂度 空间复杂度 时间复杂度 二、学习率 三、指数衰减学习率——解决lr的选择 1.总结经验 2.公式引入 四、激活函数 1.简单模型&#xff1a;最初的理解 2.激活函数MP模型 3.优秀的激活函数的特点 4.…

广州华锐视点:VR技术在航天教育的应用优势

VR技术在航天教育中的应用可以带来许多创新和优势&#xff0c;为学生和公众提供更加生动、沉浸式的航天科学学习体验。以下是VR技术在航天教育中的几个主要应用方面&#xff1a; 航天探索模拟&#xff1a;VR技术可以模拟航天探索的场景&#xff0c;让学生和公众身临其境地感受航…

FFmpeg<第一篇>:环境配置

1、官网地址 http://ffmpeg.org/download.html2、linux下载ffmpeg 下载&#xff1a; wget https://ffmpeg.org/releases/ffmpeg-snapshot.tar.bz2解压&#xff1a; tar xvf ffmpeg-snapshot.tar.bz23、FFmpeg ./configure编译参数汇总 解压 ffmpeg-snapshot.tar.bz2 之后&…

Vulnhub: DriftingBlues: 1靶机

kali&#xff1a;192.168.111.111 靶机&#xff1a;192.168.111.215 信息收集 端口扫描 nmap -A -sC -v -sV -T5 -p- --scripthttp-enum 192.168.111.215 80端口首页源码 访问noteforkingfish.txt&#xff0c;发现为Ook!加密的密文 解密后提示需要用户eric和修改hosts文件&…

【Python编程】将同一种图片分类到同一文件夹中

一、数据结构如下&#xff1a; 二、编程工具&#xff1a;Jupyter-Notebook 三、代码&#xff1a; import os import cv2 import shutilpath0os.getcwd()\\apple\\RGB path1os.getcwd()\\apple\\tof_confidence path2os.getcwd()\\apple\\tof_depth path3os.getcwd()\\apple\\…

教你写出高质量函数,简单又实用

在编写函数时&#xff0c;程序员通常需要遵循以下步骤进行&#xff1a; 1、确定最佳的设计逻辑是编写函数时应该考虑的重要因素。这些因素包括设计合理的数据结构、算法和逻辑封装&#xff0c;并且还要考虑到用户的安全因素。挑战在于确保所设计的方案既满足客户需求&#xff…

药品不良反应数据库有哪些?记住这个就够用了

药品不良反应数据库的作用是收集、记录和分析药品使用过程中可能出现的不良反应信息。这些数据库通常由医药监管机构、医药数据公司、医疗机构和科研机构等建立和维护&#xff0c;在监测和评估药品的安全性、发现和确认新的不良反应、支持药品监管决策、提供医学信息和警示等方…

【局部活动轮廓】使用水平集方法实现局部活动轮廓方法研究(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

会员管理系统实战开发教程04-会员开卡

我们已经用3篇篇幅介绍了会员管理的功能&#xff0c;接着就要开发会员的业务。通常我们开通会员之后需要给会员开通会员卡&#xff0c;一个会员可以有多张会员卡。 在数据源设计的时候&#xff0c;像这种一个会员有多张会员卡的&#xff0c;我们称之为一对多的关系&#xff0c…

Sentinel流量控制与熔断降级

&#x1f4dd; 学技术、更要掌握学习的方法&#xff0c;一起学习&#xff0c;让进步发生 &#x1f469;&#x1f3fb; 作者&#xff1a;一只IT攻城狮 &#xff0c;关注我&#xff0c;不迷路 。 &#x1f490;学习建议&#xff1a;1、养成习惯&#xff0c;学习java的任何一个技术…

Levels - 场景参考:山脉景观(Landscape Mountains)

一些从前的笔记的归档&#xff0c;记录了一些UE4资产的相关信息&#xff1b; 山脉景观&#xff08;Landscape Mountains&#xff09;&#xff1a; 项目的地形材质比较复杂&#xff0c;有几个比较重要的大效果功能&#xff0c;一个是沉积岩的效果&#xff1a; 沉积岩效果的功能…

聊聊NFC技术

目录 1.什么是NFC 2.NFC技术的原理 3.NFC技术的应用 4.NFC技术对生活的影响 1.什么是NFC NFC&#xff08;Near Field Communication&#xff09;是一种短距离无线通信技术&#xff0c;可在10厘米以内进行点对点的数据传输。它是基于RFID&#xff08;Radio Frequency Identif…

基于OpenCV实战(基础知识二)

目录 简介 1.ROI区域 2.边界填充 3.数值计算 4.图像融合 简介 OpenCV是一个流行的开源计算机视觉库&#xff0c;由英特尔公司发起发展。它提供了超过2500个优化算法和许多工具包&#xff0c;可用于灰度、彩色、深度、基于特征和运动跟踪等的图像处理和计算机视觉应用。Ope…

IDEA 出现问题:.gitgnore忽略文件失效解决方案

❤️作者主页&#xff1a;小虚竹 ❤️作者简介&#xff1a;大家好,我是小虚竹。Java领域优质创作者&#x1f3c6;&#xff0c;CSDN博客专家&#x1f3c6;&#xff0c;华为云享专家&#x1f3c6;&#xff0c;掘金年度人气作者&#x1f3c6; ❤️技术活&#xff0c;该赏 ❤️点赞…