对数组各个独立区间的处理,首先想到差分法。下面是对差分法的解释:
差分法
1、定义
待处理的数组为a[1...n],增加第0项和第n+1项,其中a[0]=0,a[n+1]=0,差分数组为d[0...n+1],其中的项满足d[i]=a[i]-a[i-1],d[0]=a[0]=0;
2、性质
如果要对a[i...j]中所有元素+1,一个一个遍历a会导致时间复杂度很大,TimeLimited,但是我们可以对差分数组的d[i]+1,d[j+1]-1,即可完成区间内所有元素的+1操作。
3、例子
由此可见对区间操作的复杂度大大减小,只需要对区间头和区间尾+1的两个差分数组元素操作,再进行一次整合即可。有人可能这个时候说还不如我直接对a进行操作,还更简单一点,可是假如数组a很大,有上万个数据,要循环很多次,每次循环处理很多个数组区间,那直接对原数组操作,必然会超时,大数据点过不去,这时差分数组就非常有必要了。
综上所述:该方法适用于区间频繁修改,而且区间范围比较大的题目
本题题解(差分法):
1、思路
1、用STL库中的unique()函数去重,这里解释一下unique()函数:
有一个数组a[1...n],unique(a,a+n),表示删去相邻的重复元素,通常需要先sort(a,a+n),CSDN上很多文章说的并不对,并且千篇一律的说“去重后把重复的元素藏在最后”,这句话是错的!
去重之后并不是把重复的元素藏在了最后, 而是只将不重复的元素排在数组最前边,直接将排序后的数组后面的元素不变。举个例子方便理解unique()函数:
这题用unique()可以适当减少运算的次数,因为两个连续的0和一个0起到的划分非零段作用是一样的比如说:
3 3 1 2 0 0 3 2 2 4 4 5 0 2
两个0很容易理解,消去一个0,当p等于3时将小于3的元素都变成0,序列变成:
3 3 0 0 0 0 3 0 0 4 4 5 0 0
可以看出多个连续的相同元素和一个单一元素是等价的。
但是注意一般unique去重需要sort排序,这题不用,记得区别!
/* CCF202109-2 非零段划分 */
//1、差分法(先消去相邻重复元素,再从unique后的数组第一位到最后一位,依次比较数字与相邻数的大小,并对d操作)
#include <bits/stdc++.h>
using namespace std;
const int maxn=5e5+2,num=1e4+2;
int a[maxn],d[num]={0};
int main(){
int n;
cin>>n;
for(int i=1;i<=n;i++) cin>>a[i];
a[0]=a[n+1]=0;
int k=unique(a,a+n+2)-a-1;//k为序列去重后,最后一个元素(0)的数组下标
/*
想象成海平面问题,海平面一开始为num高,慢慢往下降的时候会有岛屿露出来
如果第i个岛屿比他两边岛屿海拔都高 ,则降到a[i]高度时岛屿数量++
如果第i个岛屿比他两边岛屿都低,则降到a[i]高度时,第i个岛屿将左右两边的岛屿连到一起,岛屿数量--
如果第i个岛屿比他一边岛屿高,一边岛屿低,则降到a[i]时,岛屿数量不变
*/
for(int i=1;i<k;i++){
if(a[i]>a[i-1]&&a[i]>a[i+1]) d[a[i]]++;
else if(a[i]<a[i-1]&&a[i]<a[i+1]) d[a[i]]--;
}
int res=0;int sum=0;
//d[i]表示海平面降到i的高度时,岛屿数量的变化,从大到小求后缀和,后缀和最大即为结果
for(int i=num;i>=0;i--){
sum+=d[i];
res=max(res,sum);
}
cout<<res;
return 0;
}
方法二、离散法
先将原数组离散,存入vector二维数组中,二维数组每一行该行行号对应的数字,在原数组中的序列,二维数组第一列表示出现原数组中的数字(除了0),然后从小到大,一层一层的判断res和sum,得到最终结果,举个例子就能很清楚的看懂:
/* CCF202109-2 非零段划分 */
//2、数组离散化并map映射(用一个二维动态数组,每一行表示1 2 3 ...n,每一列表示数字1 2 3...n所在的原数组序号)
#include<bits/stdc++.h>
using namespace std;
const int maxn=5e5+2,num=1e4+2;
vector<int> m[num];
int a[maxn];
int main(){
int n;
cin>>n;
int maxnum=0;
for(int i=1;i<=n;i++) {
cin>>a[i];
if(a[i]>maxnum) maxnum=a[i];
}
a[0]=a[n+1]=0;
n=unique(a,a+n+2)-a;
int res=0;
bool flag=false;
for(int i=0;i<n;i++){
if(a[i]!=0){
m[a[i]].push_back(i);
}
if(flag&&a[i]==0){
res++;
flag=false;
}else{
flag=true;
}
}
int sum=res;
for(int i=1;i<=maxnum;i++){
for(int j=0;j<m[i].size();j++){
if(a[m[i][j]]<a[m[i][j]-1]&&a[m[i][j]]<a[m[i][j]+1]){
sum++;
}
else if(a[m[i][j]]>a[m[i][j]-1]&&a[m[i][j]]>a[m[i][j]+1]){
sum--;
}
}
res=max(sum,res);
}
cout<<res;
return 0;
}