《从入门到精通:蓝桥杯编程大赛知识点全攻略》(九)-连号区间数、递增三元组

news/2025/2/13 1:49:11/

前言

在本篇博客中,我们将通过模拟方法解决两道经典的算法题:连号区间数和递增三元组。通过这两道题,我们将展示如何通过模拟操作和细致的算法设计来解决实际问题,帮助提升算法思维和编码能力。


连号区间数

小明这些天一直在思考这样一个奇怪而有趣的问题:
在 1∼N 的某个排列中有多少个连号区间呢?

这里所说的连号区间的定义是:如果区间 [L,R] 里的所有元素(即此排列的第 L 个到第 R 个元素)递增排序后能得到一个长度为 R−L+1 的“连续”数列,则称这个区间连号区间。
当 N很小的时候,小明可以很快地算出答案,但是当 N 变大的时候,问题就不是那么简单了,现在小明需要你的帮助。
输入格式
第一行是一个正整数 N,表示排列的规模。
第二行是 N 个不同的数字 Pi,表示这 N 个数字的某一排列。
输出格式
输出一个整数,表示不同连号区间的数目。
数据范围
1≤N≤10000,1≤Pi≤N
输入样例1:

java">4
3 2 4 1

输出样例1:

java">7

输入样例2:

java">5
3 4 2 5 1

输出样例2:

java">9

样例解释
第一个用例中,有 7 个连号区间分别是:[1,1],[1,2],[1,3],[1,4],[2,2],[3,3],[4,4]

第二个用例中,有 9 个连号区间分别是:[1,1],[1,2],[1,3],[1,4],[1,5],[2,2],[3,3],[4,4],[5,5]

算法思路

暴力做法,只需要枚举每一个区间的左端点和区间的右端点,然后分别对每一个区间进行排序,之后再对区间的元素进行遍历,当区间的后一个元素比前一个元素多一时,说明是一种方案。
这样的时间复杂度为O(n^3logn),会超时,我们可以对区间内的元素是否是等差数列进行优化。
将区间的最大值和最小值记录一下,当区间的最大值-区间的最小值=右区间-左区间(即max- min == j-i)时就说明该方案是合格的。
此时时间复杂度是O(n^2),能够通过。

代码如下

暴力做法

java">import java.util.*;public class Main {public static void main(String[] args) {Scanner sc = new Scanner(System.in);int n = sc.nextInt();int[] arr = new int[n+1]; // 区间下标从1开始,所以n+1for (int i=1; i<=n; i++) {arr[i] = sc.nextInt();}int res = 0;for (int i=1; i<=n; i++) {for (int j=i; j<=n; j++) {int[] temp = arr.clone(); // 开一个新数组,便于每次新数组排序Arrays.sort(temp, i, j+1); // 把新数组中[i,j]排序boolean flag = true;for (int k=i; k<j; k++) {if (temp[k+1] - temp[k] != 1) {flag = false; break;}}if (flag == true) {res ++;}}}System.out.println(res);}
}

优化版本

java">import java.io.*;public class Main {static PrintWriter pw = new PrintWriter(new OutputStreamWriter(System.out));static BufferedReader br = new BufferedReader(new InputStreamReader(System.in));static StreamTokenizer st = new StreamTokenizer(br);static int mod = 100000007;static int N = 10010;static int[] arr = new int[N];public static void main(String[] args) throws Exception {int n = nextInt();for(int i = 1; i <= n; i++){arr[i] = nextInt();}int res = 0;for(int i = 1; i <= n; i++){//枚举区间左端点int max = Integer.MIN_VALUE;int min = Integer.MAX_VALUE;for(int j = i; j <= n; j++){//枚举区间右端点max = Math.max(max, arr[j]);//区间最大值min = Math.min(min, arr[j]);//区间最小值if(max - min == j - i){res++;}}}pw.println(res);pw.flush();}public static int nextInt()throws Exception{st.nextToken();return (int)st.nval;}
}

递增三元组

给定三个整数数组

A=[A1,A2,…AN],B=[B1,B2,…BN],C=[C1,C2,…CN],
请你统计有多少个三元组 (i,j,k) 满足:

  • 1≤i,j,k≤N
  • Ai<Bj<Ck

输入格式
第一行包含一个整数 N。
第二行包含 N 个整数 A1,A2,…AN。
第三行包含 N 个整数 B1,B2,…BN。
第四行包含 N 个整数 C1,C2,…CN。
输出格式
一个整数表示答案。
数据范围
1≤N≤105,0≤Ai,Bi,Ci≤105
输入样例:

java">3
1 1 1
2 2 2
3 3 3

输出样例:

java">27

算法思路

在这里插入图片描述
暴力做法直接枚举3层循环i、j、j,然后做判断满足 Ai < Bj && Bj < Ck的情况方案数加1即可,但会超时。
根据题意需要求有多少个i、j、k满足Ai<Bj<Ck,故就是求对于B数组中的每一个数Bj,在A数组中有多少个数小于Bj,在C数组中有多少个数大于Bj。
我们可以用前缀和的思路来处理这道题。对于在A中有多少个数小于Bj,用数组cnt来存储A数组中每个数出现在的次数即cnt[i]表示i这个值在A数组中出现的次数,然后求cnt数组的前缀和数组s,s[i] = cnt[0] + cnt[1]+…+cnt[i]表示在A数组中0~i出现的次数,最后求在A数组中有多少个数小于Bj即s[Bj - 1]。
求在C数组中有多少个数大于Bj,同样用前缀和的方法开处理,先将原本的cnt和s两个数组都清0,然后遍历C数组,用cnt数组统计C数组中每个值出现的次数即cnt[i]表示i这个值在C数组中出现的次数,然后求cnt数组的前缀和数组s,s[i]表示在C数组中0~i出现的次数,要求在C数组中有多少个数大于Bj即s[N -1] - s[Bj]。
最后将每一个Bj对应在A数组中有多少个数小于Bj,在C数组中有多少个数大于Bj两个结果相乘即可。

N = 100010,因为数组中数组的最大值是105,需要求大于Bj的值出现的次数,需要用0 ~ 10000所有数出现的次数即s[N-1]减去0~Bj的数出现的次数即s[Bj]就是大于Bj的数出现的次数。

因为数组中每个值的范围是0~105,同时我们需要统计cnt数组的前缀和数组,但此时cnt[0]表示数组中0出现的次数,而s[0]其实是无意义的,故做题时数组输入值时都加1,这样可以让cnt[0]变为无意义,s[0]也无意义,减少做题的复杂度。

在这里插入图片描述

代码如下

暴力枚举(会超时):

java">import java.util.*;
import java.io.*;
public class Main {static PrintWriter pw = new PrintWriter(new OutputStreamWriter(System.out));static BufferedReader br = new BufferedReader(new InputStreamReader(System.in));static StreamTokenizer st = new StreamTokenizer(br);static int N = 100010;static int n;static int[] a = new int[N];static int[] b = new int[N];static int[] c = new int[N];public static void main(String[] args)throws Exception {int n = nextInt();for(int i = 1; i <= n; i++){a[i] = nextInt();}for(int i = 1; i <= n; i++){b[i] = nextInt();}for(int i = 1; i <= n; i++){c[i] = nextInt();}long res = 0;for(int i = 1; i <= n; i++){for(int j = 1; j <= n; j++){for(int k = 1; k <= n; k++){if(a[i] < b[j] && b[j] < c[k]){res++;}}}}pw.println(res);pw.flush();}public static int nextInt() throws Exception {st.nextToken();return (int) st.nval;}}

前缀和方法代码如下:

java">
import java.io.*;
import java.util.Arrays;public class Main {static PrintWriter pw = new PrintWriter(new OutputStreamWriter(System.out));static BufferedReader br = new BufferedReader(new InputStreamReader(System.in));static StreamTokenizer st = new StreamTokenizer(br);static int N = 100010;static int n;static int[] a = new int[N];static int[] b = new int[N];static int[] c = new int[N];//as[i]在A数组中有多少个数小于b[i]static int[] as = new int[N];//cs[i]在A数组中有多少个数大于b[i]static int[] cs = new int[N];static int[] cnt = new int[N];//前缀和数组static int[] s = new int[N];public static void main(String[] args) throws Exception {n = nextInt();for (int i = 1; i <= n; i++) {a[i] = nextInt()+1;}for (int i = 1; i <= n; i++) {b[i] = nextInt()+1;}for (int i = 1; i <= n; i++) {c[i] = nextInt()+1;}//求as[]for(int i = 1; i <= n; i++){cnt[a[i]]++;}//输入的时候3个数组中的值保证了最小值是1,这样求cnt数组的前缀和的时候可以保证s[0]还是0for(int i = 1; i < N; i++){s[i] = s[i-1] + cnt[i];}for(int i = 1; i <= n; i++){as[i] = s[b[i] - 1];}//求cs[]Arrays.fill(cnt, 0);Arrays.fill(s, 0);for(int i = 1; i <= n; i++){cnt[c[i]]++;}for(int i = 1; i < N; i++){s[i] = s[i-1] + cnt[i];}for(int i = 1; i <= n; i++){cs[i] = s[N - 1] - s[b[i]];}//枚举每个b[i]long res = 0;for(int i = 1; i <= n; i++){res += (long)cs[i] * as[i];}pw.println(res);pw.flush();}public static int nextInt() throws Exception {st.nextToken();return (int) st.nval;}
}

总结

通过这两道模拟题,我们不仅实践了模拟技巧,还掌握了如何通过细致的操作和逐步验证来解决复杂问题。希望本篇博客能为大家提供解决此类题目的一些启发,提升在算法与编程上的实战能力。


http://www.ppmy.cn/news/1571577.html

相关文章

Java面试题-Redis缓存

文章目录 1.**Redis 支持哪几种数据类型&#xff1f;**2.**Redis的持久化机制是怎样的&#xff1f;**1.RDB2.AOF3.对比 3.双写一致性1.问题2.双写不一致的原因3.延迟双删&#xff08;有脏数据&#xff09;4.读写锁&#xff08;强一致&#xff09;5.异步通知&#xff08;最终一致…

C++基础知识学习记录—构造函数

1、构造函数(constructor) 作用&#xff1a;用于创建对象时给属性值进行初始化 构造函数是一个特殊的函数&#xff1a; 1、构造函数与类同名 2、如果没有显式给出构造函数&#xff0c;编译器会给出默认的构造函数(参数为空&#xff0c;并且函数体也为空)&#xff0c;并没有…

Android studio怎么创建assets目录

在Android Studio中创建assets文件夹是一个简单的步骤&#xff0c;通常用于存储不需要编译的资源文件&#xff0c;如文本文件、图片、音频等 main文件夹&#xff0c;右键new->folder-assets folder

22.3、IIS安全分析与增强

目录 IIS安全威胁分析iis安全机制iis安全增强 IIS安全威胁分析 iis是微软公司的Web服务软件&#xff0c;主要提供网页服务&#xff0c;除此之外还可以提供其他服务&#xff0c;第一个最主要的是网页服务&#xff0c;第二个是SMTP邮件服务&#xff0c;第三个是FTP文件传输服务。…

DeepSeek-R1技术革命:用强化学习重塑大语言模型的推理能力

引言&#xff1a;低成本高性能的AI新范式 在2025年1月&#xff0c;中国AI公司DeepSeek发布了两个标志性模型——DeepSeek-R1-Zero与DeepSeek-R1&#xff0c;以仅600万美元的训练成本实现了与OpenAI O1系列&#xff08;开发成本约5亿美元&#xff09;相当的推理性能&#xff0c…

集成方案 | Docusign + 纷享销客,自动化合同签署,加速业务增长!

本文将详细介绍 Docusign 与纷享销客的集成步骤及其效果&#xff0c;并通过实际应用场景来展示 Docusign 的强大集成能力&#xff0c;以证明 Docusign 集成功能的高效性和实用性。 在数字化转型过程中&#xff0c;企业依赖高效的 CRM 系统来优化业务流程和客户关系管理。纷享销…

c# OpenCvSharp 16位转8位图

通过OpenCvSharp&#xff0c;将16位图转8位图&#xff0c;代码如&#xff1a; public static Mat Convert16BitTo8Bit(Mat src16, double max, double min){if (src16.Depth() 0){//已经是8位了return src16;}Mat dst8 Mat.Zeros(src16.Rows, src16.Cols, MatType.CV_8UC1);i…

Anaconda Navigator 与 Conda:GUI 和 CLI 的对比与使用

1. 引言 Anaconda 提供了两种主要的管理工具&#xff1a; Anaconda Navigator&#xff08;GUI 界面&#xff09;Conda&#xff08;命令行工具 CLI&#xff09; 这两种工具各有优劣&#xff0c;适用于不同类型的用户。本文将详细介绍它们的功能、使用方法及对比分析&#xff…