题目链接:计算机软件能力认证考试系统
分析:我们先考虑不存在连续两个数相同的情况,当p足够大时,所有的数都会变成0,我们逐渐减少p,观察非零段变化的规律,发现对于a[1~n]若存在某个a[i]>a[i-1]且a[i]>a[i+1],那么当我们p从a[i]变成a[i]-1时,非零段会增加1,这个1就是a[i]产生的,同理当存在某个a[i]<a[i-1]且a[i]<a[i+1],那么当我们p从a[i]变成a[i]-1时,非零段会减少1,那是因为原来a[i-1]和a[i+1]是两个非零段,但是由于a[i]导致两段合成了一段从而使非零段减少1,分析到这问题基本上就解决了
现在我们来看一下如果有两个连续的数相同,那么可以发现他们要么全为0要么全非0,所以他们必然在一个非零段里面,所以可以把他们当成一个整体来看待,这样就把问题转化为了上面描述的那样。最后我们直接倒着for循环一遍即可,也就是从a数组中的最大值开始到0截止,a[i]的前缀和的最大值就是答案。
补充一点:从下向上思考也是可以的,但是我们的初始非零段应该设置为1,下面分别附上p从大到小和从小到达的代码:
p从大到小:
#include<cstdio>
#include<iostream>
#include<cstring>
#include<vector>
#include<algorithm>
#include<map>
#include<queue>
using namespace std;
vector<int>alls;
int a[500003],d[500003];
int main()
{
int n,mx=0;
cin>>n;
for(int i=1;i<=n;i++)
scanf("%d",&a[i]),mx=max(a[i],mx);
int t=1;
for(int i=1;i<=n;i++)
{
if(a[i]==a[i-1]) continue;
a[t++]=a[i];
}
n=t-1;
a[0]=a[n+1]=0;
for(int i=1;i<=n;i++)
{//预处理下降到每一个高度后的非零段的变换量
if((a[i]>a[i-1])&&(a[i]>a[i+1])) d[a[i]]++;//下降到a[i]后非零段会加1
if((a[i]<a[i-1])&&(a[i]<a[i+1])) d[a[i]]--;//下降到a[i]后非零段会减1
}
int s=0,ans=0;
for(int i=mx;i>=0;i--)
{
s+=d[i];
ans=max(ans,s);
}
printf("%d",ans);
return 0;
}
p从小到大:
#include<cstdio>
#include<iostream>
#include<cstring>
#include<vector>
#include<algorithm>
#include<map>
#include<queue>
using namespace std;
vector<int>alls;
int a[500003],d[500003];
int main()
{
int n,mx=0;
cin>>n;
memset(d,0,sizeof d);
for(int i=1;i<=n;i++)
scanf("%d",&a[i]),mx=max(a[i],mx);
int t=1;
for(int i=1;i<=n;i++)
{
if(a[i]==a[i-1]) continue;
a[t++]=a[i];
}
n=t-1;
a[0]=a[n+1]=0;
for(int i=1;i<=n;i++)//因为我们不知道一开始有多少个非零段,说以我们只能从高度大的地方向高度小的地方遍历
{//预处理下降到每一个高度后的非零段的变换量
if((a[i]>a[i-1])&&(a[i]>a[i+1])) d[a[i]]--;//上升到a[i]后非零段会加1
if((a[i]<a[i-1])&&(a[i]<a[i+1])) d[a[i]]++;//上升到a[i]后非零段会减1
}
int s=1,ans=0;//默认非零段为1
for(int i=0;i<=mx;i++)
{
s+=d[i];
ans=max(ans,s);
}
printf("%d",ans);
return 0;
}