当前位置: 首页 > news >正文

LeetCode算法日记 - Day 13: 前缀和、二维前缀和

目录

1. 前缀和

1.1 题目解析

1.2 解法

1.3 代码实现

2. 二维前缀和

2.1 题目解析

2.2 解法

2.3 代码实现


1. 前缀和

描述

对于给定的长度为 𝑛n 的数组 {𝑎1,𝑎2,…,𝑎𝑛}{a1​,a2​,…,an​} ,我们有 𝑚m 次查询操作,每一次操作给出两个参数 𝑙,𝑟l,r ,你需要输出数组中第 𝑙l 到第 𝑟r 个元素之和,即 𝑎𝑙+𝑎𝑙+1+⋯+𝑎𝑟al​+al+1​+⋯+ar​ 。

输入描述:

第一行输入两个整数 𝑛,𝑚(1≦𝑛,𝑚≦105)n,m(1≦n,m≦105) 代表数组中的元素数量、查询次数。

第二行输入 𝑛n 个整数 𝑎1,𝑎2,…,𝑎𝑛(−109≦𝑎𝑖≦109)a1​,a2​,…,an​(−109≦ai​≦109) 代表初始数组。

此后 𝑚m 行,每行输入两个整数 𝑙,𝑟(1≦𝑙≦𝑟≦𝑛)l,r(1≦l≦r≦n) 代表一次查询。

输出描述:

对于每一次查询操作,在一行上输出一个整数,代表区间和。

示例1

输入:

3 2
1 2 4
1 2
2 3

复制输出:

3
6

1.1 题目解析

这道题的核心需求是:多次查询数组某个区间的元素之和

  • 如果我们每次都从头开始遍历区间求和,那么单次查询的复杂度是O(n),在 m 次查询下会退化为 O(n*m),数据上限是 10^5,这样会超时。

  • 所以我们要寻找一种更快的“预处理 + 快速查询”的方式。

这里可以直接想到 前缀和

  • 预先计算好数组的前缀和 dp[i],表示从下标 1i 的元素和;

  • 那么某个区间 [l, r] 的和只需要 dp[r] - dp[l-1],时间复杂度瞬间降为 O(1)。

  • 因此,总体复杂度是:

    • 预处理:O(n)

    • 查询:O(m)

    • 总复杂度:O(n+m),完全可行。

1.2 解法

i)定义前缀和数组
设 dp[i] 表示数组前 i 个元素的和:

dp[i]=a1+a2+⋯+ai

递推式:

dp[i]=dp[i−1]+arr[i]

ii)区间和公式
当我们要查询 [l, r] 区间的和时:

sum(l,r)=dp[r]−dp[l−1]

  • dp[r] 表示 [1, r] 的和

  • dp[l-1] 表示 [1, l-1] 的和

  • 相减即可得到 [l, r] 的和

iii)复杂度分析

  • 预处理前缀和:O(n)

  • 每次查询:O(1)

  • 总复杂度:O(n + m),完全可以在限制内高效运行。

1.3 代码实现

import java.util.Scanner;public class Main {public static void main(String[] args) {Scanner scan = new Scanner(System.in);int n = scan.nextInt(); // 数组长度int m = scan.nextInt(); // 查询次数int[] arr = new int[n + 1];   // 原始数组 (1-based)long[] dp = new long[n + 1];  // 前缀和数组// 输入数组for (int i = 1; i <= n; i++) {arr[i] = scan.nextInt();}// 构建前缀和for (int i = 1; i <= n; i++) {dp[i] = dp[i - 1] + arr[i];}// 处理查询while (m-- > 0) {int l = scan.nextInt();int r = scan.nextInt();System.out.println(dp[r] - dp[l - 1]);}}
}

2. 二维前缀和

描述

给定一个由 𝑛n 行 𝑚m 列整数组成的矩阵 {𝑎𝑖,𝑗}{ai,j​}(下标均从 11 开始)。
现有 𝑞q 次独立查询,第 𝑘k 次查询给定四个整数 𝑥1,𝑦1,𝑥2,𝑦2x1​,y1​,x2​,y2​,表示左上角坐标 (𝑥1,𝑦1)(x1​,y1​) 与右下角坐标 (𝑥2,𝑦2)(x2​,y2​) 满足 1≦𝑥1≦𝑥2≦𝑛1≦x1​≦x2​≦n 且 1≦𝑦1≦𝑦2≦𝑚1≦y1​≦y2​≦m。
请你计算该子矩阵中全部元素之和,记为


S(𝑥1,𝑦1,𝑥2,𝑦2)=∑𝑖=𝑥1𝑥2 ∑𝑗=𝑦1𝑦2𝑎𝑖,𝑗


你需要依次回答所有查询。

输入描述:

在一行上输入三个整数 𝑛,𝑚,𝑞(1≦𝑛,𝑚≦103; 1≦𝑞≦105)n,m,q(1≦n,m≦103; 1≦q≦105),依次表示矩阵的行数、列数与查询次数。
此后 𝑛n 行,每行输入 𝑚m 个整数 𝑎𝑖,1,𝑎𝑖,2,…,𝑎𝑖,𝑚(−109≦𝑎𝑖,𝑗≦109)ai,1​,ai,2​,…,ai,m​(−109≦ai,j​≦109),表示矩阵第 𝑖i 行的元素;共计 𝑛×𝑚n×m 个整数。
此后 𝑞q 行,每行输入四个整数 𝑥1,𝑦1,𝑥2,𝑦2x1​,y1​,x2​,y2​,所有变量均满足


1≦𝑥1≦𝑥2≦𝑛,1≦𝑦1≦𝑦2≦𝑚1
 

输出描述:

对于每一次查询,在一行上输出一个整数,表示对应子矩阵元素之和。

示例1

输入:

3 4 3
1 2 3 4
3 2 1 0
1 5 7 8
1 1 2 2
1 1 3 3
1 2 3 4

复制输出:

8
25
32

2.1 题目解析

题目本质

这道题的本质就是 “多次查询一个子矩阵的元素和”
换句话说,我们需要在一个二维矩阵上,频繁地求局部和

常规解法

最直观的方法:每次查询时,枚举子矩阵 (x1​,y1​)→(x2​,y2​)把里面所有数加起来。

问题分析

  • 如果子矩阵大小接近 n×m,一次查询复杂度就是 O(nm)。

  • 在最坏情况下:n=m=1000, q=10^5

    枚举每个子矩阵元素,复杂度约为 10^{11} → 必然超时

思路转折

要想高效,就必须 在查询前进行预处理
类比一维数组的 前缀和

  • 一维前缀和把“区间和查询”优化到 O(1)O(1)O(1)。

  • 同理,二维前缀和可以把“子矩阵和查询”优化到 O(1)O(1)O(1

2.2 解法

i)构建二维前缀和矩阵 dp[i][j],表示从 (1,1)(1,1)(1,1) 到 (i,j)(i,j)(i,j) 子矩阵内所有元素的和。

ii)递推公式:

dp[i][j]=dp[i−1][j]+dp[i][j−1]−dp[i−1][j−1]+a[i][j]

iii)查询子矩阵和公式(容斥原理):

S(x1​,y1​,x2​,y2​)=dp[x2​][y2​]−dp[x1​−1][y2​]−dp[x2​][y1​−1]+dp[x1​−1][y1​−1]

步骤拆解

  • 输入矩阵 → 读入 n×m 的二维数组。

  • 构建前缀和矩阵 → 用递推公式预处理。

  • 处理查询 → 每个查询用容斥公式计算子矩阵和。

  • 输出结果 → 每次查询 O(1)O(1)O(1)。

2.3 代码实现

import java.util.Scanner;public class Main {public static void main(String[] args) {Scanner in = new Scanner(System.in);int n = in.nextInt();   // 行数int m = in.nextInt();   // 列数int q = in.nextInt();   // 查询次数int[][] arr = new int[n + 1][m + 1];long[][] dp = new long[n + 1][m + 1];// 读入矩阵for (int i = 1; i <= n; i++) {for (int j = 1; j <= m; j++) {arr[i][j] = in.nextInt();}}// 构建二维前缀和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];}}// 处理查询while (q-- > 0) {int x1 = in.nextInt();int y1 = in.nextInt();int x2 = in.nextInt();int y2 = in.nextInt();long ans = dp[x2][y2] - dp[x1 - 1][y2] - dp[x2][y1 - 1] + dp[x1 - 1][y1 - 1];System.out.println(ans);}}
}

http://www.lryc.cn/news/623793.html

相关文章:

  • es下载、安装、部署以及集成和mysql数据同步
  • **守护进程(Daemon)** 是一种在后台运行的特殊进程
  • 为什么神经网络在长时间训练过程中会存在稠密特征图退化的问题
  • Linux中聚合链路与软件网桥配置指南
  • 深入了解linux系统—— 线程控制
  • AI 编程在老项目中的困境与改进方向
  • 【Linux | 网络】高级IO
  • 63.不同路径
  • 分治-归并-315.计算右侧小于当前元素的个数-力扣(LeetCode)
  • C++ vector的使用
  • C语言(12)——进阶函数
  • 北京JAVA基础面试30天打卡12
  • 语音转文字,如何提升内容创作效率?
  • 智能汽车领域研发,复用云原始开发范式?
  • WebSocket--精准推送方案(二):实时消息推送-若依项目示例
  • 在职老D渗透日记day19:sqli-labs靶场通关(第26a关)get布尔盲注 过滤or和and基础上又过滤了空格和注释符 ‘)闭合
  • 【架构师从入门到进阶】第五章:DNSCDN网关优化思路——第十一节:网关安全-对称与非对称加密
  • 告别“测试滞后”:AI实时测试工具在敏捷开发中的落地经验
  • 【165页PPT】锂电池行业SAP解决方案(附下载方式)
  • 自动驾驶中的传感器技术34——Lidar(9)
  • 定时器中断点灯
  • 记一次安装OpenStack(Stein)-nova报错问题解决
  • 42 C++ STL模板库11-容器4-forward_list
  • 利用标准IO实现寻找文件中字符出现最多次数
  • Opencv 形态学与梯度运算
  • python的软件工程与项目管理课程组学习系统
  • 【LeetCode题解】LeetCode 33. 搜索旋转排序数组
  • Android studio gradle有关设置
  • 一周学会Matplotlib3 Python 数据可视化-多子图及布局实现
  • java之 junit4单元测试Mockito的使用