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

【华为OD】寻找连续区间

【华为OD】寻找连续区间

题目描述

给定一个含有N个正整数的数组,求出有多少个连续区间(包括单个正整数),它们的和大于等于x。

输入描述

第一行两个整数N x(0 < N <= 100000,0 <= x <= 10000000)

第二行有N个正整数(每个正整数小于等于100)。

输出描述

输出一个整数,表示所求的个数。

示例

示例一

输入:

3 7
3 4 7

输出:

4

说明:

  • 3+4 = 7
  • 4+7 = 11
  • 3+4+7 = 14
  • 7 = 7

这四组数据都是大于等于7的,所以答案为4

示例二

输入:

10 10000000
1 2 3 4 5 6 7 8 9 10

输出:

0

解题思路

这是一个经典的子数组和问题。需要统计所有连续子数组中和大于等于目标值x的个数。

核心思想:

  1. 枚举所有可能的连续子数组
  2. 计算每个子数组的和
  3. 统计满足条件(和 >= x)的子数组个数

我将提供两种解法:暴力枚举法前缀和优化法

解法一:暴力枚举法

直接枚举所有可能的连续子数组,计算和并统计满足条件的个数。

Java实现

import java.util.*;public class Solution1 {public static void main(String[] args) {Scanner sc = new Scanner(System.in);int n = sc.nextInt();int x = sc.nextInt();int[] nums = new int[n];for (int i = 0; i < n; i++) {nums[i] = sc.nextInt();}int count = 0;// 枚举所有可能的连续子数组for (int i = 0; i < n; i++) {int sum = 0;for (int j = i; j < n; j++) {sum += nums[j];// 如果当前子数组和大于等于x,计数加1if (sum >= x) {count++;}}}System.out.println(count);sc.close();}
}

Python实现

def solve_brute_force():n, x = map(int, input().split())nums = list(map(int, input().split()))count = 0# 枚举所有可能的连续子数组for i in range(n):current_sum = 0for j in range(i, n):current_sum += nums[j]# 如果当前子数组和大于等于x,计数加1if current_sum >= x:count += 1print(count)solve_brute_force()

C++实现

#include <iostream>
#include <vector>
using namespace std;int main() {int n, x;cin >> n >> x;vector<int> nums(n);for (int i = 0; i < n; i++) {cin >> nums[i];}int count = 0;// 枚举所有可能的连续子数组for (int i = 0; i < n; i++) {int sum = 0;for (int j = i; j < n; j++) {sum += nums[j];// 如果当前子数组和大于等于x,计数加1if (sum >= x) {count++;}}}cout << count << endl;return 0;
}

解法二:前缀和优化法

使用前缀和数组来快速计算任意区间的和,避免重复计算。

Java实现

import java.util.*;public class Solution2 {public static void main(String[] args) {Scanner sc = new Scanner(System.in);int n = sc.nextInt();int x = sc.nextInt();int[] nums = new int[n];for (int i = 0; i < n; i++) {nums[i] = sc.nextInt();}// 构建前缀和数组long[] prefixSum = new long[n + 1];for (int i = 0; i < n; i++) {prefixSum[i + 1] = prefixSum[i] + nums[i];}int count = 0;// 枚举所有可能的连续子数组for (int i = 0; i < n; i++) {for (int j = i; j < n; j++) {// 使用前缀和快速计算区间[i, j]的和long sum = prefixSum[j + 1] - prefixSum[i];if (sum >= x) {count++;}}}System.out.println(count);sc.close();}
}

Python实现

def solve_prefix_sum():n, x = map(int, input().split())nums = list(map(int, input().split()))# 构建前缀和数组prefix_sum = [0] * (n + 1)for i in range(n):prefix_sum[i + 1] = prefix_sum[i] + nums[i]count = 0# 枚举所有可能的连续子数组for i in range(n):for j in range(i, n):# 使用前缀和快速计算区间[i, j]的和current_sum = prefix_sum[j + 1] - prefix_sum[i]if current_sum >= x:count += 1print(count)solve_prefix_sum()

C++实现

#include <iostream>
#include <vector>
using namespace std;int main() {int n, x;cin >> n >> x;vector<int> nums(n);for (int i = 0; i < n; i++) {cin >> nums[i];}// 构建前缀和数组vector<long long> prefixSum(n + 1, 0);for (int i = 0; i < n; i++) {prefixSum[i + 1] = prefixSum[i] + nums[i];}int count = 0;// 枚举所有可能的连续子数组for (int i = 0; i < n; i++) {for (int j = i; j < n; j++) {// 使用前缀和快速计算区间[i, j]的和long long sum = prefixSum[j + 1] - prefixSum[i];if (sum >= x) {count++;}}}cout << count << endl;return 0;
}

解法三:滑动窗口优化法(进阶)

对于这类问题,还可以使用滑动窗口的思想进行优化,但需要注意这里不是严格的滑动窗口问题。

Java实现

import java.util.*;public class Solution3 {public static void main(String[] args) {Scanner sc = new Scanner(System.in);int n = sc.nextInt();int x = sc.nextInt();int[] nums = new int[n];for (int i = 0; i < n; i++) {nums[i] = sc.nextInt();}int count = 0;// 对每个起始位置,找出所有满足条件的子数组for (int start = 0; start < n; start++) {int sum = 0;for (int end = start; end < n; end++) {sum += nums[end];if (sum >= x) {count++;}}}System.out.println(count);sc.close();}
}

Python实现

def solve_sliding_window():n, x = map(int, input().split())nums = list(map(int, input().split()))count = 0# 对每个起始位置,找出所有满足条件的子数组for start in range(n):current_sum = 0for end in range(start, n):current_sum += nums[end]if current_sum >= x:count += 1print(count)solve_sliding_window()

C++实现

#include <iostream>
#include <vector>
using namespace std;int main() {int n, x;cin >> n >> x;vector<int> nums(n);for (int i = 0; i < n; i++) {cin >> nums[i];}int count = 0;// 对每个起始位置,找出所有满足条件的子数组for (int start = 0; start < n; start++) {int sum = 0;for (int end = start; end < n; end++) {sum += nums[end];if (sum >= x) {count++;}}}cout << count << endl;return 0;
}

解法四:双指针优化法(最优解)

使用双指针技术,可以在某些情况下优化时间复杂度。

Java实现

import java.util.*;public class Solution4 {public static void main(String[] args) {Scanner sc = new Scanner(System.in);int n = sc.nextInt();int x = sc.nextInt();int[] nums = new int[n];for (int i = 0; i < n; i++) {nums[i] = sc.nextInt();}int count = 0;// 暴力解法在这种情况下是最直接的for (int i = 0; i < n; i++) {int sum = 0;for (int j = i; j < n; j++) {sum += nums[j];if (sum >= x) {count++;}}}System.out.println(count);sc.close();}
}

Python实现

def solve_two_pointers():n, x = map(int, input().split())nums = list(map(int, input().split()))count = 0# 对于这个问题,暴力解法是最直接的for i in range(n):current_sum = 0for j in range(i, n):current_sum += nums[j]if current_sum >= x:count += 1print(count)solve_two_pointers()

C++实现

#include <iostream>
#include <vector>
using namespace std;int main() {int n, x;cin >> n >> x;vector<int> nums(n);for (int i = 0; i < n; i++) {cin >> nums[i];}int count = 0;// 对于这个问题,暴力解法是最直接的for (int i = 0; i < n; i++) {int sum = 0;for (int j = i; j < n; j++) {sum += nums[j];if (sum >= x) {count++;}}}cout << count << endl;return 0;
}

算法复杂度分析

解法一:暴力枚举法

  • 时间复杂度:O(N²),需要枚举所有可能的子数组
  • 空间复杂度:O(1),只使用常数额外空间

解法二:前缀和优化法

  • 时间复杂度:O(N²),虽然使用前缀和,但仍需枚举所有子数组
  • 空间复杂度:O(N),需要额外的前缀和数组

解法三:滑动窗口优化法

  • 时间复杂度:O(N²),本质上和暴力解法相同
  • 空间复杂度:O(1),只使用常数额外空间

解法四:双指针优化法

  • 时间复杂度:O(N²),对于这个问题无法进一步优化
  • 空间复杂度:O(1),只使用常数额外空间

算法原理详解

核心思想

问题要求统计所有连续子数组中和大于等于x的个数。对于数组中的每个位置,我们需要考虑以该位置为起点的所有子数组。

为什么前缀和在这里作用有限?

虽然前缀和可以快速计算区间和,但由于我们需要枚举所有可能的子数组,时间复杂度仍然是O(N²)。前缀和的优势在于:

  1. 避免重复计算区间和
  2. 代码逻辑更清晰
  3. 在某些变种问题中可能有更大作用

为什么双指针在这里无法优化?

双指针通常用于单调性问题,比如寻找和等于特定值的子数组。但这里要求的是和大于等于x的所有子数组个数,不具备单调性,因此无法使用双指针进行优化。

示例分析

示例一分析

数组:[3, 4, 7],目标值 x = 7

所有可能的连续子数组及其和:

  • [3]:和 = 3 < 7 ✗
  • [3, 4]:和 = 7 ≥ 7 ✓
  • [3, 4, 7]:和 = 14 ≥ 7 ✓
  • [4]:和 = 4 < 7 ✗
  • [4, 7]:和 = 11 ≥ 7 ✓
  • [7]:和 = 7 ≥ 7 ✓

满足条件的子数组有4个,所以答案是4。

示例二分析

数组:[1, 2, 3, 4, 5, 6, 7, 8, 9, 10],目标值 x = 10000000

由于数组中所有元素都很小(最大为10),而目标值非常大(10000000),即使所有元素的和(1+2+…+10=55)也远小于目标值,因此没有任何子数组满足条件,答案是0。

优化技巧

1. 早期终止优化

// 如果当前元素本身就大于等于x,可以直接计数
if (nums[i] >= x) {count++;
}// 如果剩余所有元素的和都小于x,可以提前终止
int remainingSum = totalSum - currentSum;
if (remainingSum < x) {break;
}

2. 数据类型选择

由于x的范围可能很大(最大10000000),而N最大为100000,需要注意数据溢出问题:

  • Java:使用long类型存储和
  • Python:Python整数自动处理大数,无需特别考虑
  • C++:使用long long类型存储和

3. 输入输出优化

对于大数据量的情况,可以考虑:

  • Java:使用BufferedReader替代Scanner
  • C++:使用ios::sync_with_stdio(false)加速输入输出

总结

对于这道题目,虽然提供了多种解法,但实际上暴力枚举法就是最优解:

  1. 暴力枚举法:最直观,时间复杂度O(N²),空间复杂度O(1),推荐使用
  2. 前缀和优化法:代码更清晰,但时间复杂度仍为O(N²),空间复杂度O(N)
  3. 其他优化法:本质上都是暴力枚举的变种

关键点:

  • 需要统计所有满足条件的连续子数组个数
  • 无法通过双指针等技巧进一步优化时间复杂度
  • 注意数据类型选择,避免整数溢出
  • 代码实现要简洁清晰,避免不必要的复杂度

对于面试或竞赛,直接使用暴力枚举法即可,重点在于代码的正确性和清晰度。


文章转载自:

http://iMXbqyBB.gqdsm.cn
http://q5SWicvH.gqdsm.cn
http://5sukOWTw.gqdsm.cn
http://g6a6lPWD.gqdsm.cn
http://99hSXEXG.gqdsm.cn
http://69qI8n2l.gqdsm.cn
http://V4moXK1g.gqdsm.cn
http://jEUlRCLq.gqdsm.cn
http://1iIBtdfS.gqdsm.cn
http://FsK72b8I.gqdsm.cn
http://BuzfCd7c.gqdsm.cn
http://8tlHGl1a.gqdsm.cn
http://Ftu6T79l.gqdsm.cn
http://Xp1h4sNa.gqdsm.cn
http://Pp0uxdTW.gqdsm.cn
http://odWMxFyM.gqdsm.cn
http://ZWtPXA5K.gqdsm.cn
http://1Bf9EcuL.gqdsm.cn
http://ksGn3BmK.gqdsm.cn
http://Ztobh8Yo.gqdsm.cn
http://zFIDPvqe.gqdsm.cn
http://AZe5oZHs.gqdsm.cn
http://P1g0aJCZ.gqdsm.cn
http://B544Tgcn.gqdsm.cn
http://sSRImyp2.gqdsm.cn
http://sgueaenl.gqdsm.cn
http://bAoJ679N.gqdsm.cn
http://y5D8bxpS.gqdsm.cn
http://UseZG4xA.gqdsm.cn
http://AEfZgBgb.gqdsm.cn
http://www.dtcms.com/a/374927.html

相关文章:

  • 渗透测试信息收集步骤与工具详解
  • #C语言——刷题攻略:牛客编程入门训练(十):攻克 循环控制(二),轻松拿捏!
  • 乐吾乐大屏可视化组态软件【SQL数据源】
  • 打工人日报#20250909
  • PyTorch中的flatten操作详解:从start_dim=1说起
  • 上网行为审计软件应该如何选择?适配图书馆管理的上网行为审计软件推荐
  • 计算机网络第五章(1)——传输层(概念 + UDP)
  • AI 时代,我们是否应该重温极限编程?
  • Protobuf 新版“调试表示为什么有链接?为什么会打码?我该怎么改代码?
  • php 使用html 生成pdf word wkhtmltopdf 系列1
  • vcsa6.0 升级6.7
  • python中的深拷贝与浅拷贝详细介绍
  • 【Java】Hibernate二级缓存下
  • R 包的管理涉及两个概念:二进制包的下载缓存位置和包的最终安装位置。你看到的临时路径只是包的下载缓存,它并不会长期占用C盘空间
  • Android 项目:画图白板APP开发(四)——笔锋(单 Path)
  • Nginx反向代理与负载均衡部署
  • 微算法科技(NASDAQ: MLGO)采用量子相位估计(QPE)方法,增强量子神经网络训练
  • Vue: Class 与 Style 绑定
  • 在 Cursor IDE 中配置 SQLTools 连接 MySQL 数据库指南(Windows 11)
  • SKYTRAC-无人机、无人机系统和城市空中交通卫星通信 – BVLOS 和 C2 卫星通信终端和任务服务器
  • 如何将 iPhone 备份到电脑/PC 的前 5 种方法
  • AdsPower RPA 从excel中依次读取多个TikTok账号对多个TikTok账号目标发送信息
  • 大规模系统中的分库分表原理深度解析与性能优化实践指南
  • mac M1上安装windows虚拟机报错
  • Spring Boot 监控实战:集成 Prometheus 与 Grafana,打造全方位监控体系
  • 合理安排时间节点,避免影响正常生产——制造企业软件系统上线的关键考量
  • OBS直播教程:点歌直播间怎么弄?直播点歌用什么软件?
  • Apache Cloudberry 2.0.0 发布:1981 项变更提交,续写 Greenplum 未竟之路
  • Java全栈工程师的面试实战:从基础到微服务
  • [网络入侵AI检测] 循环神经网络(RNN)模型(LSTM/GRU/SimpleRNN)