题目
题目背景
语文考试结束了,成绩还是一如既往地有问题。
题目描述
语文老师总是写错成绩,所以当她修改成绩的时候,总是累得不行。她总是要一遍遍地给某些同学增加分数,又要注意最低分是多少。你能帮帮她吗?
输入格式
第一行有两个整数 n,p,代表学生数与增加分数的次数。
第二行有 n 个数,1∼a1∼an,代表各个学生的初始成绩。
接下来 p 行,每行有三个数,x,y,z,代表给第 x 个到第 y 个学生每人增加 z 分。
输出格式
输出仅一行,代表更改分数后,全班的最低分。
输入输出样例
输入 #1复制
3 2 1 1 1 1 2 1 2 3 1
输出 #1复制
2
说明/提示
对于 40%40% 的数据,有 n≤103。
对于 60%60% 的数据,有 n≤104。
对于 80%80% 的数据,有 n≤105。
对于 100%100% 的数据,有 n≤5×106,p≤n,学生初始成绩 ≤100,z≤100。
思路
按照题目的意思是给一个区间加上一个数值,最后在进行遍历来求解最后的结果,但是他的时间复杂度为O(n*n),已经超出了题目给出的范围(计算机一秒运算次)
此方法的代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int M=5*1e6+10;
int h[M];
int main(){
ios::sync_with_stdio(false);//解除输入输出的限制
cin.tie(0);
cout.tie(0);
int n,p;
cin>>n>>p;
for(int i=1;i<=n;i++)
cin>>h[i];
while(p--){
int a,b,c;
cin>>a>>b>>c;
for(int j=a;j<=b;j++)
h[j]+=c;
}
int minn=10000000;
for(int i=1;i<=n;i++)
minn=min(minn,h[i]);
cout<<minn;
return 0;
}
在洛谷的评测结果
可以看出有两个测试点的无法通过。
我们来对以上方法来进行优化一下,时间复杂度比较高的原因是因为我们在一个区间进行加减时多了一重for循环,而且都是重复的操作,我们来对其进行优化。
这里我们引入差分数组来进行优化:
先来了解一下差分数组
假如原数组为
2 3 6 4 8 9 2 1
他的差分数组就为
2 1 3 -2 4 1 -7 -1
通过观察可以知道差分数组为当前原数组和前一个数组的差,如果从开始就把差分数组进行累加你就会发现他们的和与原数组是相等的,可以看成原数组的i的值就等于1至i的累加和(我们也叫前缀和)使得原数组的i的值与差分数组的1至i的值产生关系,那么对差分数组中i的值进行+C的话,就会使得原数组i到n的值都会+C,我们就可以实现影响差分数组的一个值来原数组的一个区间的值进行影响。
如何对a,b区间的值进行改变呢?
当我在差分数组a处进行+c时,a至n原数组的值都+c,而题目要求的是a到b的区间,其实只要b+1处-c就可以了,把b+1至n多加的c给减掉就行了。实现了通过改变两个点来对一个区间的加减操作,时间复杂度减低为O(n).
代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int M=5*1e6+10;
int h[M];
int main(){
ios::sync_with_stdio(false);//解除输入输出的限制
cin.tie(0);
cout.tie(0);
int n,p;
cin>>n>>p;
int temp=0;
for(int i=1;i<=n;i++){
int a;
cin>>a;
h[i]=a-temp;//构建差分数组
temp=a;//更新当前值
}
while(p--){
int a,b,c;
cin>>a>>b>>c;
h[a]+=c;//在a处加一相当于给原数组的a到n的范围加c;
h[b+1]-=c;//在b处加一相当于给原数组的b+1到的n的范围减去c实现了只给a到b区间加c的操作
}
int sum=0;
int minn=1000000;
for(int i=1;i<=n;i++){//遍历求出最小数
sum+=h[i];
minn=min(minn,sum);
}
cout<<minn;
return 0;
}
评测结果