(CCF202109-2)非零段划分(差分)

本文解析了计算机软件能力认证考试中的一个问题,涉及如何通过分析数列中非零段的变化来确定前缀和的最大值。关键策略在于识别连续数的组合效应和非连续数对非零段的影响。核心算法利用递增或递减的p值调整来追踪非零段增减。
摘要由CSDN通过智能技术生成

题目链接:计算机软件能力认证考试系统

分析:我们先考虑不存在连续两个数相同的情况,当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;
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值