2018蓝桥杯|算法训练|区间K大数查询
问题描述
给定一个序列,每次询问序列中第l个数到第r个数中第K大的数是哪个。
输入格式
第一行包含一个数n,表示序列长度。
第二行包含n个正整数,表示给定的序列。
第三个包含一个正整数m,表示询问个数。
接下来m行,每行三个数l,r,K,表示询问序列从左往右第l个数到第r个数中,从大往小第K大的数是哪个。序列元素从1开始标号。
输出格式
总共输出m行,每行一个数,表示询问的答案。
样例输入
5 1 2 3 4 5 2 1 5 2 2 3 2
样例输出
4 2
数据规模与约定
对于30%的数据,n,m<=100;
对于100%的数据,n,m<=1000;
保证k<=(r-l+1),序列中的数<=106。
我的答案:
1 #include<iostream> 2 using namespace std; 3 int* max(int *a,int l,int r){ 4 int count=0; 5 int flag=0; 6 int *b=new int[r-l+1]; 7 int *visit=new int[r]; 8 for(int i=0;i<r;i++){ 9 visit[i]=0; 10 } 11 for(int i=l-1;i<r;i++){ 12 int maximum=-100; 13 for(int ii=l-1;ii<r;ii++){ 14 if(a[ii]>maximum&&visit[ii]==0){ 15 maximum=a[ii]; 16 flag=ii; 17 } 18 } 19 visit[flag]=1; 20 b[count++]=maximum; 21 22 } 23 return b; 24 } 25 int result (int *a,int l,int r, int k){ 26 int *b= max(a,l,r); 27 return b[k-1]; 28 } 29 int main(){ 30 int num; 31 cin>>num; 32 int *shu=new int[num]; 33 for(int i=0;i<num;i++){ 34 cin>>shu[i]; 35 } 36 int geshu; 37 cin>>geshu;//enquire the number 38 int l,r,k; 39 for(int j=0;j<geshu;j++){ 40 cin>>l>>r>>k; 41 cout<<result(shu,l,r,k)<<endl; 42 } 43 44 delete shu; 45 return 0; 46 }
思路:这是一道给出一段序列,求其子序列的第几大数的问题。我的思路是把这个区间上的元素从大到小一个一个拿出来放入一个新的数组中,第几大就是取数组的第几个元素。在这里需要一个visit数组记录元素是否已经放入新的数组中。(还要一种思路是先把这个区间上的元素放到新的数组中,再从大到小排个序,这样就可以使用选择排序(每次选一个最小的放后面),就不用visit数组了,而且会比这个算法优化一些。)
出现的问题:
在一开始我还用选择排序的思路在做,但选择排序的方法会改变原序列的排序,这样区间数据就改变了,就会导致结果的错误。
还有一个非常关键而易错的地方在于max函数中的visit数组的置零。因为在样例中就可以看到这个max函数执行了两次,如果不写这个置零的话,虽然定义visit数组时默认值全为零,但因为第一次执行这个函数导致visit数组的部分元素的值变为了1,但正确的情况下是每执行完下一次开始的时候visit数组元素值都要变成0。因此给visit数组的每一个元素置零的步骤必不可少。
缺点:时间复杂度 O(n2)