csp真题 202109-2非零段划分C++代码(100分)

试题编号: 202109-2
试题名称: 非零段划分
时间限制: 1.0s
内存限制: 512.0MB
在这里插入图片描述
在这里插入图片描述

样例1输入

11
3 1 2 0 0 2 0 4 5 0 2

样例1输出

5

样例1解释
p=2时,A=[3,0,2,0,0,2,0,4,5,0,2],5个非零段依次为[3]、[2]、[2]、[4、5]和[2];此时非零段个数达到最大。

样例2输入

14
5 1 20 10 10 10 10 15 10 20 1 5 10 15

样例2输出

4

样例2解释
p=12时,A=[0,0,20,0,0,0,0,15,0,20,0,0,0,15],4个非零段依次为[20]、[15]、[20]和[15];此时非零段个数达到最大。

样例3输入

3
1 0 0

样例3输出

1

样例3解释
p=1时,A=[1,0,0],此时仅有1个非零段[1],非零段个数达到最大。

样例4输入

3
0 0 0

样例4输出

0

样例4解释
无论p取何值,A都不含有非零段,故非零段个数至多为0。
在这里插入图片描述
70分代码及思路:
思路:
1.将所有数据存储到一个数组A中,并记录最大值max_A;
2.p从1开始遍历到最大值max_A,记录每一次p值下的非零段的个数最大值到数组max1中。
3.判断非零段个数:遍历数组,找到每一个不为0的值,如果该数前一个数为0或者该数是数组的第一个数,非零段个数加1。
4.遍历数组max1,输出最大值。
分析:
该方法属于常规思路,比较简单,无任何算法优化,有很多循环遍历,且有双层循环,由题目给出的数据范围,在双层循环中,循环次数有可能达到10^9,显然会超时。

#include <iostream> 
using namespace std;

int main() 
{
	int n;
	int i=0,p=0,max_A=0,max=0;
	
	cin>>n;
	int A[n]={0};
	for(i=0;i<n;i++)
	{
		cin>>A[i];
		if(A[i]>max_A)	//记录数组A中的最大值 
		{
			max_A=A[i];
		}
	}
	int max1[max_A]={0};	//开辟数组用来存储每一个p值下的非零段的最大值 
	for(p=1;p<max_A;p++)	//p从1开始遍历 
	{
		for(i=0;i<n;i++)	//将数组中小于p的数置为0 
		{
			if(A[i]<p)
			{
				A[i]=0;
			}
		}
		for(i=0;i<n;i++)	//遍历数组 
		{
			if(A[i]!=0){
			//若不为0,判断是否是数组的第一个数或者该数的前一个数为0,如果是,非零段加1 
				if(i==0||A[i-1]==0){
					max=max+1;
				}
			}
		}
		max1[p]=max;
		max=0;
	}
	for(i=1;i<max_A;i++)	
		if(max1[i]>max){
			max=max1[i];
		}
	cout<<max;
	return 0;
}

提交结果如下:
在这里插入图片描述
如果要拿到满分,那么用常规的思路逻辑肯定不行,必须要做一定的优化处理,在参考b站视频教程后整理如下:
1.读入数组时插入set,对数组进行去重和排序,(方便p值的选取)。
2.利用向量vector,将set集合中每个元素出现的位置存储在同一个vector向量中。
3.统计初始的非零段个数作为参考值。
(与前一种方法有点区别,在数组开头和最后加0,只需判断当前数不为0并且前一位数为0,非零段个数加1即可)
4.p依次从set中取值(从小到大)(如果值为0,跳过),依据位置向量获得每个值在数组A中的位置。
将对应位置的值赋值为0
如果该位置的前后的值都大于0,则非零段数加1;
如果该位置的前后的值都等于0,则非零段数减1;
其他情况下非零段个数不变。

更新非零段的最大值。
该方法的优点在于:
通过向量对应的位置坐标,判断坐标位置前后元素的值进行判断即可,不必在每一个p值下都对数组进行循环遍历,降低了时间复杂度。
此方法思路来源b站,可参考b站视频内容

满分代码:

#include <bits/stdc++.h>
using namespace std;

int A[500002]= {0};
vector<int> Vector[10001];
set<int> Set;

int main() {
	int n;
	cin>>n;
	for(int i=1; i<=n; i++) {
		cin>>A[i];
		Set.insert(A[i]);
		Vector[A[i]].push_back(i);
	}

	int number=0;	//非零段个数
	for(int i=1; i<=n; i++) {	//找出当前非零段的个数
		if((A[i]>0)&&(A[i-1]==0))
			number++;
	}

	int temp;
	int number_max;	//非零段个数的最大值
	temp=number_max=number;

	set<int>::iterator p=Set.begin();//用迭代器来访问set集合
	//p的值从集合开始遍历
	if(*p==0)		//如果集合中的元素为0,则直接从下一个元素开始
		p++;
	for(p; p!=Set.end(); p++) {
		vector<int> &Vector1=Vector[*p];	//引用Vector[*p]为Vector1 
		for(int i=0; i<Vector1.size(); i++) {	//集合中该元素出现的总次数
			int k=Vector1[i];	
			A[k]=0;
			if((A[k-1]!=0)&&(A[k+1]!=0))
				temp++;
			if((A[k-1]==0)&&(A[k+1]==0))
				temp--;
		}
		number_max=max(number_max,temp);
	}
	cout<<number_max<<endl;
	return 0;

}

提交结果如下:
在这里插入图片描述

  • 5
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值