【算法基础】(一)基础算法 --- 前缀和与差分

news/2024/4/19 14:33:24/文章来源:https://blog.csdn.net/m0_67660672/article/details/129122468

✨个人主页:bit me
✨当前专栏:算法基础
🔥专栏简介:该专栏主要更新一些基础算法题,有参加蓝桥杯等算法题竞赛或者正在刷题的铁汁们可以关注一下,互相监督打卡学习 🌹 🌹 🌹

前 缀 和 与 差 分

  • 🎄一. 前缀和(一维)
  • 🌲二. 子矩阵的和(二维)
  • 🌳三. 差分
  • 🌴四. 差分矩阵
  • 🎋五. 总结

🎄一. 前缀和(一维)

输入一个长度为 n 的整数序列。
 
接下来再输入 m 个询问,每个询问输入一对 l,r。
 
对于每个询问,输出原序列中从第 l 个数到第 r 个数的和。

输入格式:

第一行包含两个整数 n 和 m。
 
第二行包含 n 个整数,表示整数数列。
 
接下来 m 行,每行包含两个整数 l 和 r,表示一个询问的区间范围。

输出格式:

共 m 行,每行输出一个询问的结果。

数据范围:

1 ≤ l ≤ r ≤n,
 
1 ≤ n,m ≤ 100000,
 
−1000 ≤ 数列中元素的值 ≤ 1000

输入样例:

5 3
2 1 3 6 4
1 2
1 3
2 4

输出样例:

3
6
10

思路:

  • 数组 a[1] + a[2] + … + a[i],对于某一个区间[l,r]的和就是s[r]-s[l-1]
  • 考虑边界统一问题可以让 s[0] = 0 统一格式,但是我们题解可以让边界从角标 1 开始,有效避免让 s[0] = 0 来单独处理

题解:

  1. 把数都遍历到数组里
Scanner scan  = new Scanner(System.in);
int n = scan.nextInt();
int m = scan.nextInt();
int[] a = new int[n+1];
int[] s = new int[n+1];
for(int i = 1 ; i <= n ; i ++ ){a[i] = scan.nextInt();
}
  1. 前 n 项数组和
for(int i = 1 ; i <= n ; i ++){s[i] = s[i-1] + a[i];
}
  1. 根据规律某一个区间[l,r]的和就是s[r]-s[l-1]
while(m-- > 0){int l = scan.nextInt();int r = scan.nextInt();System.out.println(s[r] - s[l-1]);
}

 
附上总的代码

import java.util.Scanner;
public static void main(String[] args){Scanner scan  = new Scanner(System.in);int n = scan.nextInt();int m = scan.nextInt();int[] a = new int[n+1];int[] s = new int[n+1];for(int i = 1 ; i <= n ; i ++ ){a[i] = scan.nextInt();}for(int i = 1 ; i <= n ; i ++){s[i] = s[i-1] + a[i];}while(m-- > 0){int l = scan.nextInt();int r = scan.nextInt();System.out.println(s[r] - s[l-1]);}
}

 

🌲二. 子矩阵的和(二维)

输入一个 n 行 m 列的整数矩阵,再输入 q 个询问,每个询问包含四个整数x1,y1,x2,y2,表示一个子矩阵的左上角坐标和右下角坐标。
 
对于每个询问输出子矩阵中所有数的和。

输入格式:

第一行包含三个整数 n,m,q。
 
接下来 n 行,每行包含 m 个整数,表示整数矩阵。
 
接下来 q 行,每行包含四个整数 x1,y1,x2,y2,表示一组询问。

输出格式:

共 q 行,每行输出一个询问的结果。

数据范围:

1 ≤ n,m ≤1000,
 
1 ≤ q ≤ 200000,
 
1 ≤ x1 ≤ x2 ≤n,
 
1 ≤ y1 ≤ y2 ≤m,
 
−1000 ≤ 矩阵内元素的值 ≤ 1000

输入样例:

3 4 3
 
1 7 2 4
 
3 6 2 8
 
2 1 2 3
 
1 1 2 2
 
2 1 3 4
 
1 3 3 4

输出样例:

17
 
27
 
21

思路:

此处视图就不画了,我们要先了解清楚计算的公式

  • s[i,j] = s[i - 1,j] + s[i,j - 1] - s[i - 1,j - 1] + a[i,j]
  • (x1, y1),(x2, y2) 这一矩阵中所有数的和 = s[x2,y2] - s[x2,y1 - 1] - s[x1 - 1,y2] + s[x1 - 1,y1 - 1]

题解:

  1. 继续按照上题一样,角标都从 1 开始,因此数组都要扩容 + 1
Scanner scan = new Scanner(System.in);
int n = scan.nextInt();
int m = scan.nextInt();
int q = scan.nextInt();
int[][] a = new int[n+1][m+1];
int[][] s = new int[n+1][m+1];
for(int i = 1 ; i <= n  ; i ++ ){for(int j = 1 ;j <= m ; j ++ ){a[i][j] = scan.nextInt();}
}
  1. 按照思路计算二维数组每一块从 (1,1) 到 (i,j) 的大小,得出 s[i,j] 的通式
for(int i = 1 ; i <= n  ; i ++ ){for(int j = 1 ;j <= m ; j ++ ){s[i][j] = s[i-1][j] + s[i][j-1] - s[i-1][j-1] + a[i][j];}
}
  1. 通式计算区域相加减的出的面积
while(q-->0){int x1 = scan.nextInt();int y1 = scan.nextInt();int x2 = scan.nextInt();int y2 = scan.nextInt();System.out.println(s[x2][y2] - s[x1-1][y2] - s[x2][y1-1] + s[x1-1][y1-1]);
}

 
附上总的代码

import java.util.Scanner;
public static void main(String[] args){Scanner scan = new Scanner(System.in);int n = scan.nextInt();int m = scan.nextInt();int q = scan.nextInt();int[][] a = new int[n+1][m+1];int[][] s = new int[n+1][m+1];for(int i = 1 ; i <= n  ; i ++ ){for(int j = 1 ;j <= m ; j ++ ){a[i][j] = scan.nextInt();}}for(int i = 1 ; i <= n  ; i ++ ){for(int j = 1 ;j <= m ; j ++ ){s[i][j] = s[i-1][j] + s[i][j-1] - s[i-1][j-1] + a[i][j];}}while(q-->0){int x1 = scan.nextInt();int y1 = scan.nextInt();int x2 = scan.nextInt();int y2 = scan.nextInt();System.out.println(s[x2][y2] - s[x1-1][y2] - s[x2][y1-1] + s[x1-1][y1-1]);}
}

 

🌳三. 差分

输入一个长度为 n 的整数序列。
 
接下来输入 m 个操作,每个操作包含三个整数 l,r,c,表示将序列中 [l,r] 之间的每个数加上 c。
 
请你输出进行完所有操作后的序列。

输入格式:

第一行包含两个整数 n 和 m。
 
第二行包含 n 个整数,表示整数序列。
 
接下来 m 行,每行包含三个整数 l,r,c,表示一个操作。

输出格式:

共一行,包含 n 个整数,表示最终序列。

数据范围:

1 ≤ n,m ≤ 100000,
 
1 ≤ l ≤ r ≤ n,
 
−1000 ≤ c ≤ 1000,
 
−1000 ≤ 整数序列中元素的值 ≤ 1000

输入样例:

6 3
1 2 2 1 2 1
1 3 1
3 5 1
1 6 1

输出样例:

3 4 5 3 4 2

思路:

  • 差分是求前缀和的逆操作,如果想将a数组中 [l,r] 部分的数据全部加上c,只需要将 b[l] + c,然后 b[r+1] - c 即可。
  • 差分操作和前缀和一样数组下标都从1开始。b[l] + c 后,l后面的数组都会加 c。r 后面的数据也会被改变,要改回来就得 b[r+1] - c
  • 求a[i]的值: 其实就是求b数组的一位前缀和

题解:

  1. 先解决范围问题
static int N = 1000010;
static int[] a = new int[N];
static int[] b = new int[N];
  1. 数据遍历进数组
Scanner scan = new Scanner(System.in);
int n = scan.nextInt();
int m = scan.nextInt();
for(int i = 1 ; i <= n ; i ++ ){a[i] = scan.nextInt();
}
  1. 构造一下b数组,因为a是b数组的前缀和
for(int i = 1 ; i <= n ; i ++ ){b[i] = a[i] - a[i - 1];
}
  1. 将a数组中 [l,r] 部分的数据全部加上c
while(m -- > 0){int l = scan.nextInt();int r = scan.nextInt();int c = scan.nextInt();b[l] += c;b[r + 1] -= c;
}
  1. 最后求一遍差分数组的前缀和
for(int i = 1 ; i <= n ; i ++ ){b[i] += b[i - 1];System.out.print(b[i] + " ");
}

 
附上总的代码

import java.util.*;public class Test {static int N = 1000010;static int[] a = new int[N];static int[] b = new int[N];public static void main(String[] args){Scanner scan = new Scanner(System.in);int n = scan.nextInt();int m = scan.nextInt();for(int i = 1 ; i <= n ; i ++ ){a[i] = scan.nextInt();}for(int i = 1 ; i <= n ; i ++ ){b[i] = a[i] - a[i - 1];}while(m -- > 0){int l = scan.nextInt();int r = scan.nextInt();int c = scan.nextInt();b[l] += c;b[r + 1] -= c;}for(int i = 1 ; i <= n ; i ++ ){b[i] += b[i - 1];System.out.print(b[i] + " ");}}
}

 

🌴四. 差分矩阵

输入一个 n 行 m 列的整数矩阵,再输入 q 个操作,每个操作包含五个整数x1,y1,x2,y2,c,其中 (x1,y1) 和 (x2,y2) 表示一个子矩阵的左上角坐标和右下角坐标。
 
每个操作都要将选中的子矩阵中的每个元素的值加上 c。
 
请你将进行完所有操作后的矩阵输出。

输入格式:

第一行包含整数 n,m,q。
 
接下来 n 行,每行包含 m 个整数,表示整数矩阵。
 
接下来 q 行,每行包含 5 个整数 x1,y1,x2,y2,c,表示一个操作。

输出格式:

共 n 行,每行 m 个整数,表示所有操作进行完毕后的最终矩阵。

数据范围:

1 ≤ n,m ≤ 1000,
1 ≤ q ≤ 100000,
1 ≤ x1 ≤ x2 ≤ n,
1 ≤ y1 ≤ y2 ≤ m,
−1000 ≤ c ≤ 1000,
−1000 ≤ 矩阵内元素的值 ≤ 1000

输入样例:

3 4 3
1 2 2 1
3 2 2 1
1 1 1 1
1 1 2 2 1
1 3 2 3 2
3 1 3 4 1

输出样例:

2 3 4 1
4 3 4 1
2 2 2 2

思路:

  • 对差分数组操作: b[x1][y1] += c; b[x1 -1][y2] -= c;b[x2][y1 -1] -= c; b[x2 - 1][y2 - 1] += c;
  • 求a的差分数组b:b[i][j] =a[i][j] - a[i - 1][j] - a[i][j - 1] + a[i - 1][j - 1];

题解:

  1. 创建原数组 同时也是b的前缀和数组,a的差分数组
canner sc = new Scanner (System.in);
int n = sc.nextInt() , m = sc.nextInt(), q = sc.nextInt();
int[][] a = new int[1010][1010];//原数组 同时也是b的前缀和数组
int[][] b = new int[1010][1010];//a的差分数组
for(int i = 1;i <= n; i ++) {for(int j = 1; j <= m; j ++) {a[i][j] = sc.nextInt();}
}
  1. 求a的差分数组b
for(int i = 1;i <= n; i ++) {for(int j = 1; j <= m; j ++) {b[i][j] =a[i][j] - a[i - 1][j] - a[i][j - 1] + a[i - 1][j - 1];}
}
  1. 对差分数组操作
for(int i = 0; i < q; i ++) {int x1 = sc.nextInt(),y1 = sc.nextInt(),x2 = sc.nextInt(),y2 = sc.nextInt() ,c = sc.nextInt();b[x1][y1] += c;b[x2 + 1][y1] -= c;b[x1][y2 + 1] -= c;b[x2 + 1][y2 + 1] += c;
}
  1. 再对b数组求一遍前缀和数组 并输出
for(int i = 1;i <= n; i ++) {for(int j = 1; j <= m; j ++) {a[i][j] = b[i][j] + a[i - 1][j] + a[i][j - 1] - a[i - 1][j - 1];System.out.print(a[i][j] + " ");}System.out.println();
}

 
附上总的代码

import java.util.*;
public class Main{
public static void main(String[] args) {Scanner sc = new Scanner (System.in);int n = sc.nextInt() , m = sc.nextInt(), q = sc.nextInt();int[][] a = new int[1010][1010];int[][] b = new int[1010][1010];for(int i = 1;i <= n; i ++) {for(int j = 1; j <= m; j ++) {a[i][j] = sc.nextInt();}}for(int i = 1;i <= n; i ++) {for(int j = 1; j <= m; j ++) {b[i][j] =a[i][j] - a[i - 1][j] - a[i][j - 1] + a[i - 1][j - 1];}}for(int i = 0; i < q; i ++) {int x1 = sc.nextInt(),y1 = sc.nextInt(),x2 = sc.nextInt(),y2 = sc.nextInt() ,c = sc.nextInt();b[x1][y1] += c;b[x2 + 1][y1] -= c;b[x1][y2 + 1] -= c;b[x2 + 1][y2 + 1] += c;}for(int i = 1;i <= n; i ++) {for(int j = 1; j <= m; j ++) {a[i][j] = b[i][j] + a[i - 1][j] + a[i][j - 1] - a[i - 1][j - 1];System.out.print(a[i][j] + " ");}System.out.println();}
}
}

 

🎋五. 总结

简单的对于一维、二维以及三维的前缀和和差分的计算公式做一个简单的整理:
这里要知道对于n维的前缀和或者差分有 2^n 项

  • 前缀和

一维前缀和: s[i] = s[i-1] + a[i]
 
求[l, r]区间的和:s[r] - s[l-1]
 
二维前缀和:s[i][j] = s[i-1][j] + s[i][j-1] - s[i-1][j-1] + a[i][j]
 
求[x1, y1] 到 [x2, y2]的和: s[x2][y2] - s[x1-1][y2] + s[x2][y1-1] - s[x1-1][y1-1]

  • 差分

一维差分: 将[l, r]上的所有数+c :b[l] += c , b[r+1] -= c
 
求a[i]的值: 其实就是求b数组的一位前缀和
 
二维差分: 将[x1, y1]到[x2, y2]上的数字+c: b[x1][x2]+=c , b[x2+1][y1] -= c , b[x1][y2+1] -=c , b[x2+1][y2+1] +=c
 
求a[i]的值: 其实就是求b数组的二维前缀和

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

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

相关文章

前端页面jquery规范写法

使用最新版本的 jQuery 最新版本的 jQuery 会改进性能和增加新功能,若不是为了兼容旧浏览器,建议使用最新版本的 jQuery。以下是三条常见的 jQuery 语句,版本越新,性能越好: $(.elem) $(.elem, context) context.find(.elem) 结果 1.6.2 版执行次数远超两个老版本。 jQ…

一文解决Rust字符串:String,str,String,str,CString,CStr

一、str和&str和String的区别 1.存放位置&#xff0c;可变不可变&#xff1a; str来源于Rust本身的数据类型&#xff0c;而String类型来自于标准库。首先看一下str 和 String之间的区别&#xff1a;String是一个可变的、堆上分配的UTF-8的字节缓冲区。而str是一个不可变的…

ElementUI分页的实现

官网地址&#xff1a;Element - The worlds most popular Vue UI framework 第一步&#xff1a;拷贝你喜欢的分页类型放在你的组件页面需要用到的分页位置 <el-paginationsize-change"handleSizeChange"current-change"handleCurrentChange":current-p…

记一次:request请求总结

前言&#xff1a;和前端联调的时候发现前端人员请求的方式不对&#xff0c;固做此总结问题&#xff1a;request请求方式有多少种&#xff1f;答&#xff1a;Java后端查看有8种&#xff0c;spring-web中的java枚举图如下而使用PostMan查看有15种&#xff0c;如下图GET&#xff0…

【重点掌握】Java基础之Javaweb核心技术详解

都说一入Java深似海&#xff0c;从此代码是爱人&#xff0c;但是学习的过程却从来都不轻松。当下&#xff0c;越来越多的互联网企业&#xff0c;招聘Java工程师时&#xff0c;明确写道需熟练掌握JavaWeb技术。作为衔接前后端的重要一环&#xff0c;JavaWeb技术已成为程序员向大…

火热报名 | DockQuery 1.2 beta版本体验官开启招募!

DockQuery是什么&#xff1f; DockQuery 代号「天狼」&#xff0c;是图尔兹全新自研的一款专业新型数据库桌面客户端&#xff0c;专为信创背景下国内外数据库开发/管理而设计&#xff0c;全面覆盖信创数据库目录、支持国内外操作系统。 目前&#xff0c;DockQuery 仅以社区版…

【教程】GitBook Editor编写电子书

GitBook Editor电子书编写说明1、安装软件2、创建文档3、编辑文档4、生成电子书1、安装软件 下载并安装GitBook Editor软件&#xff0c;网上资源很多&#xff0c;根据自己系统选用即可 官网参考&#xff1a;GitBook - Where technical teams document. 2、创建文档 1&#xf…

Talk | 清华大学交叉信息研究院助理教授杜韬:利用计算方法探究流固耦合

本期为TechBeat人工智能社区第474期线上Talk&#xff01; 北京时间2月15日(周三)20:00&#xff0c;清华大学交叉信息研究院助理教授——杜韬的Talk将准时在TechBeat人工智能社区开播&#xff01; 他与大家分享的主题是: “利用计算方法探究流固耦合”&#xff0c;届时将介绍流固…

HTTP与HTTPS原理

目录 HTTP 协议背景 协议格式 请求行 请求报头 请求正文&#xff08;有效载荷&#xff09; 响应行 HTTPS原理 协议背景 什么是加密&#xff1f; 为什么要加密&#xff1f; 加密方式 数据摘要&#xff08;数据指纹&#xff09; 协议加密方案实现探究 方案一&#xff1a;只使用对…

【云原生】初识Kubernetes的理论基础

一、kubernetes概述 1.1 kubernetes介绍 K8S的全称为Kubernetes (K12345678S)&#xff0c;首字母与尾字母中间有8个字母&#xff0c;缩写为K8S 作用 用于自动部署、扩展和管理“容器化(containerized) 应用程序”的开源系统。可以理解成K8S是负责自动化运维管理多个容器化程序…

【云原生】k8s之Yaml文件详解

一、K8S支持的文件格式 kubernetes支持YAML和JSON文件格式管理资源对象。 JSON格式&#xff1a;主要用于api接口之间消息的传递YAML格式&#xff1a;用于配置和管理&#xff0c;YAML是一种简洁的非标记性语言&#xff0c;内容格式人性化&#xff0c;较易读 1、yaml和json的主…

[Golang实战]github.io部署个人博客hugo[新手开箱可用][小白教程]

[Golang实战]github.io部署个人博客hugo[新手开箱可用][小白教程]1.新手教程(小白也能学会)2.开始准备2.1myBlog是hugo的项目1.安装Hugo2.创建hugo项目2.2 xxxx.github.io是github.io中规定的pages项目3.成功部署4.TODO自动化workflows部署github.io1.新手教程(小白也能学会) …

分析| 2023年移动开发平台的发展空间

春节过后返工已经过月&#xff0c;许多移动开发领域的企业都在忙着做技术调研与选型。在此之前&#xff0c;不如先回顾一下2022年的市场趋势&#xff0c;再结合好的移动开发平台的标准&#xff0c;从中窥见2023年的发展前景。 Gartner十大战略技术趋势 全球权威咨询机构Gartne…

分析称勒索攻击在非洲、中东与中国增长最快

Orange Cyberdefense&#xff08;OCD&#xff09;于 2022 年 12 月 1 日发布了最新的网络威胁年度报告。报告中指出&#xff0c;网络勒索仍然是头号威胁 &#xff0c;也逐渐泛滥到世界各地。 报告中的网络威胁指的是企业网络中的某些资产被包括勒索软件在内的攻击进行勒索&…

2022-06-16_555时基的迷人历史和先天缺陷!

https://www.eet-china.com/news/magazine220608.html 555时基的迷人历史和先天缺陷&#xff01; 发布于2022-06-16 03:39:12 LARRY STABILE 流行数十年的555时基&#xff0c;业内不知晓的工程师应该寥寥无几&#xff01;几乎所有的数字电路教材中&#xff0c;都有该芯片的身影…

LeetCode 周赛 333,你管这叫 Medium 难度?

本文已收录到 AndroidFamily&#xff0c;技术和职场问题&#xff0c;请关注公众号 [彭旭锐] 提问。 大家好&#xff0c;我是小彭。 上周是 LeetCode 第 333 场周赛&#xff0c;你参加了吗&#xff1f;这场周赛质量很高&#xff0c;但难度标得不对&#xff0c;我真的会谢。算法…

基于龙芯 2K1000 的嵌入式 Linux 系统移植和驱动程序设计

2.1 需求分析 本课题以龙芯 2K1000 处理器为嵌入式系统的处理器&#xff0c;需要实现一个完成的嵌 入式软件系统&#xff0c;系统能够正常启动并可以稳定运行嵌入式 Linux。设计网络设备驱 动&#xff0c;可以实现板卡与其他网络设备之间的网络连接和文件传输。设计 PCIE 设备驱…

重温一下C#的时间类型,并简单写一个定时器功能

&#x1f389;&#x1f389; 时间是一个非常抽象的概念&#xff0c;本篇文章我们不深究目前电脑上的时候是如何保持全网同步。主要是讲讲在使用C#编程语言里的时间类型。最后使用定时任务简单写一个提醒功能&#xff0c;比如&#xff1a;每天10点准时打开一次csdn首页&#xff…

yolov5源码解读--数据处理模块

yolov5源码解读--数据处理模块加载数据读取图片加载标签马赛克数据增强图片标签其他的数据增强变图像变标签__getitem__构建Batch加载数据 create_dataloader 跳转到datasets.py文件中&#xff0c;可以看到支持输入的文件类型非常丰富。。 回归正题 跳转LoadImagesAndLabel…

分析JEP 290机制的Java实现

简介 https://openjdk.org/jeps/290 Filter Incoming Serialization Data过滤传入的序列化数据 JEP290是Java官方提供的一套来防御反序列化的机制&#xff0c;其核心在于提供了一个ObjectInputFilter接口&#xff0c;通过设置filter对象&#xff0c;然后在反序列化&#xff…