文章目录
58. 区间和(第九期模拟笔试)
题目链接:
题目描述:
题目描述
给定一个整数数组 Array,请计算该数组在每个指定区间内元素的总和。
输入描述
第一行输入为整数数组 Array 的长度 n,接下来 n 行,每行一个整数,表示数组的元素。随后的输入为需要计算总和的区间下标:a,b (b > = a),直至文件结束。
输出描述
输出每个指定区间内元素的总和。
输入示例
5
1
2
3
4
5
0 1
1 3
输出示例
3
9
提示信息
数据范围:
0 < n <= 100000
暴力解法:(会超时)
#include <bits/stdc++.h>
using namespace std;
int main(){
int n;
cin>>n;
vector<int> arr(n); // 使用 vector 来动态分配数组
for(int i=0;i<n;i++){
cin>>arr[i];
}
int a,b;
while(cin>>a>>b){
int sum = 0;
for(int j =a;j<=b;j++){
sum+=arr[j];
}
cout<<sum<<endl;
}
}
区间和思想ACM模式代码:
import java.util.Scanner;
public class Main{
public static void main(String args[]){
Scanner scanner = new Scanner(System.in);
int n = scanner.nextInt();//从键盘读入输入整数n;
int[] vec = new int[n];//定义数组vec用于存放初始数据;
int[] p = new int[n];//定义p数组用于存区间和;
int presum = 0;//前缀和;
for(int i=0;i<n;i++){
vec[i] = scanner.nextInt();//循环从键盘读入n个数据存进vec数组
presum+=vec[i];//计算前缀和
p[i] = presum;//将每个位置对应的前缀和存入p数组对应的位置
}
//前面准备工作做好了,进入真正的计算环节:
while(scanner.hasNext()){
int sum = 0;//结果和
int a = scanner.nextInt();//键盘读入a
int b = scanner.nextInt();//键盘读入b
if(a==0){
sum = p[b];
}else sum = p[b] - p[a-1];
System.out.println(sum);
}
scanner.close();
}
}
思路:
暴力解法的代码一提交,发现超时了.....
因为题目作者在制作本题的时候,特别制作了大数据量查询,卡的就是这种暴力解法。
来举一个极端的例子,如果我查询m次,每次查询的范围都是从0 到 n - 1
那么该算法的时间复杂度是 O(n * m) m 是查询的次数
如果查询次数非常大的话,这个时间复杂度也是非常大的。
接下来我们来引入前缀和,看看前缀和如何解决这个问题。
前缀和的思想是重复利用计算过的子数组之和,从而降低区间查询需要累加计算的次数。
前缀和 在涉及计算区间和的问题时非常有用!
前缀和的思路其实很简单,我给大家举个例子很容易就懂了。
例如,我们要统计 vec[i] 这个数组上的区间和。
我们先做累加,即 p[i] 表示 下标 0 到 i 的 vec[i] 累加 之和。
如图:
如果,我们想统计,在vec数组上 下标 2 到下标 5 之间的累加和,那是不是就用 p[5] - p[1] 就可以了。
为什么呢?
p[1] = vec[0] + vec[1];
p[5] = vec[0] + vec[1] + vec[2] + vec[3] + vec[4] + vec[5];
p[5] - p[1] = vec[2] + vec[3] + vec[4] + vec[5];
这不就是我们要求的 下标 2 到下标 5 之间的累加和吗。
如图所示:
p[5] - p[1] 就是 红色部分的区间和。
而 p 数组是我们之前就计算好的累加和,所以后面每次求区间和的之后 我们只需要 O(1) 的操作。
特别注意: 在使用前缀和求解的时候,要特别注意 求解区间。
如上图,如果我们要求 区间下标 [2, 5] 的区间和,那么应该是 p[5] - p[1],而不是 p[5] - p[2]。
如果在使用前缀和的时候,分不清前缀和的区间,建议画一画图,模拟一下 思路会更清晰。
注:本文章参考代码随想录,下面是出处:思路原文