试题编号: 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;}
提交结果如下: