第十二届蓝桥杯省赛 C/C++大学B组 试题I:双向排序

试题题目
在这里插入图片描述
本题为编程题第四题
解题思路:
1、分析一
  首先看到了排序,根据题目意思:
  <1>、当输入 ( 0 (0 (0 S e a t ) Seat) Seat)时,将数列从 a 1 a_1 a1 a S e a t a_{Seat} aSeat进行降序排列;
  <2>、当输入 ( 1 (1 (1 S e a t ) Seat) Seat)时,将数列从 a S e a t a_{Seat} aSeat a n a_n an进行升序排列;
  利用快速排序算法: Q u i c k s o r t ( i n t Quicksort(int Quicksort(int ∗ a , i n t *a,int a,int l e f t , i n t left,int left,int r i g h t , i n t right,int right,int t y p e ) type) type),测试代码可以看到运行超时,对于数列数以及操作数量非常大时,显然不合适。对于排序算法,已经无法实现优化了,我们只能对输入的数据进行优化。代码如下:

#include<stdio.h>
int N[1000005];
void Quicksort(int *,int,int,int);
int main(){
	int Length,Times,Type,Seat;
	int i,j,L,R;
	scanf("%d %d",&Length,&Times);
	for(i=1; i<=Length; i++){ N[i] = i;}
	for(i=0; i<Times; i++){
		scanf("%d %d",&Type,&Seat);
		Type == 1 ? ( L = Seat, R = Length ) : ( L = 1, R = Seat );
		Quicksort(N,L,R,Type);
	}
	for(j=1;j<=Length;j++){
		printf("%d ",N[j]);
	}
	return 0;
}

2、分析二
  需要对输入数据进行分析,应当考虑输入数据的规律、数量、种类、方式等特性,根据特性优化程序。对于本题目分析发现输入数据是存在着某种规律的,用于简化实际对序列的操作步骤。分析规律如下:
  <1>、当输入数据的操作类型为连续的 1 1 1(升序)时,应当去其中的参数最小的那一个操作,将本组操作合并成了一个有效操作。
  解释: ( 1 , a 3 ) (1,a_3) (1,a3) 操作,将序列从 A r r y a 3 Arry_{a_3} Arrya3 A r r y n Arry_n Arryn升序排列; ( 1 , a 1 ) (1,a_1) (1,a1) 操作,将序列从 A r r y a 1 Arry_{a_1} Arrya1 A r r y n Arry_n Arryn升序排列; ( 1 , a 2 ) (1,a_2) (1,a2)操作,将序列从 A r r y a 2 Arry_{a_2} Arrya2 A r r y n Arry_n Arryn升序排列; 在 a 1 < a 2 < a 3 a_1<a_2<a_3 a1<a2<a3的情况下,连续的三个操作,等价于一个操作 ( 1 , a 1 ) (1,a_1) (1,a1)

  <2>、当输入数据的操作类型为连续的 0 0 0(降序)时,应当去其中的参数最大的那一个操作,将本组操作合并成了一个有效操作。
  解释: ( 0 , a 1 ) (0,a_1) (0,a1) 操作,将序列从 A r r y 1 Arry_1 Arry1 A r r y a 1 Arry_{a_1} Arrya1降序排列; ( 0 , a 3 ) (0,a_3) (0,a3) 操作,将序列从 A r r y 1 Arry_1 Arry1 A r r y a 3 Arry_{a_3} Arrya3降序排列; ( 0 , a 2 ) (0,a_2) (0,a2)操作,将序列从 A r r y 1 Arry_1 Arry1 A r r y a 2 Arry_{a_2} Arrya2降序排列; 在 a 1 < a 2 < a 3 a_1<a_2<a_3 a1<a2<a3的情况下,连续的三个操作,等价于一个操作 ( 0 , a 3 ) (0,a_3) (0,a3)

  <3>、当输入数据的操作类型为交替的,第一个操作类型为 0 0 0(降序),第二个操作类型为 1 1 1(升序),第三个操作类型为 0 0 0(降序)。如果第三个参数大于等于第一个参数时,那么第一个操作和第二个操作均为无效操作。
  解释:第一步 ( 0 , a 1 ) (0,a_1) (0,a1) 操作,将序列从 A r r y 1 Arry_1 Arry1 A r r y a 1 Arry_{a_1} Arrya1降序排列;第二步 ( 1 , a 2 ) (1,a_2) (1,a2) 操作,将序列从 A r r y a 2 Arry_{a_2} Arrya2 A r r y n Arry_n Arryn升序排列;第三步 ( 0 , a 3 ) (0,a_3) (0,a3)操作,将序列从 A r r y 1 Arry_1 Arry1 A r r y a 3 Arry_{a_3} Arrya3降序排列; 在 a 1 < a 2 < a 3 a_1<a_2<a_3 a1<a2<a3的情况下,连续的三个操作,等价于一个操作 ( 0 , a 3 ) (0,a_3) (0,a3) 。即当类型为 0 0 0时,本次输入的参数需要与上一个同类型的参数对比,大于等于它时,上两步操作无效。
  <4>、当输入数据的操作类型为交替的,第一个操作类型为 1 1 1(升序),第二个操作类型为 0 0 0(降序),第三个操作类型为 1 1 1(升序)。如果第三个参数小于等于第一个参数时,那么第一个操作和第二个操作均为无效操作。
  解释:第一步 ( 1 , a 3 ) (1,a_3) (1,a3) 操作,将序列从 A r r y a 3 Arry_{a_3} Arrya3 A r r y n Arry_n Arryn升序排列;第二步 ( 0 , a 2 ) (0,a_2) (0,a2) 操作,将序列从 A r r y 1 Arry_1 Arry1 A r r y a 2 Arry_{a_2} Arrya2降序排列;第三步 ( 1 , a 1 ) (1,a_1) (1,a1)操作,将序列从 A r r y a 1 Arry_{a_1} Arrya1 A r r y n Arry_n Arryn升序排列; 在 a 1 < a 2 < a 3 a_1<a_2<a_3 a1<a2<a3的情况下,连续的三个操作,等价于一个操作 ( 1 , a 1 ) (1,a_1) (1,a1) 。即当类型为 1 1 1时,本次输入的参数需要与上一个同类型的参数对比,小于等于它时,上两步操作无效。
  <5>、观察发现,初始的序列 A r r y 1 Arry_1 Arry1 A r r y 2 Arry_2 Arry2 A r r y 3 Arry_3 Arry3 A r r y n Arry_n Arryn,一定是一个升序列,相当于操作了 ( 1 , 1 ) (1,1) (1,1)。那么首次输入操作时,本次操作应当与该操作作比较。
  便于理解见下表:

1234
第一个操作 ( 1 , a 3 ) (1,a_3) (1,a3) ( 0 , a 1 ) (0,a_1) (0,a1) ( 0 , a 1 ) (0,a_1) (0,a1) ( 1 , a 3 ) (1,a_3) (1,a3)
第二个操作 ( 1 , a 1 ) (1,a_1) (1,a1) ( 0 , a 3 ) (0,a_3) (0,a3) ( 1 , a 2 ) (1,a_2) (1,a2) ( 0 , a 2 ) (0,a_2) (0,a2)
第三个操作 ( 1 , a 2 ) (1,a_2) (1,a2) ( 0 , a 2 ) (0,a_2) (0,a2) ( 0 , a 3 ) (0,a_3) (0,a3) ( 1 , a 1 ) (1,a_1) (1,a1)
有效操作 ( 1 , a 1 ) (1,a_1) (1,a1) ( 0 , a 3 ) (0,a_3) (0,a3) ( 0 , a 3 ) (0,a_3) (0,a3) ( 1 , a 1 ) (1,a_1) (1,a1)

2、程序设计
  <1>、设计一个结构体数组,存储每一步的实际操作。初始时,结构体数组第0项,类型和参数均为 1 1 1,用指针 T o p Top Top 指向即将输入的结构体数组项数。
  <2>、输入第一行,确定序列长度与操作次数。
  <3>、循环输入数据。输入时首先与上一步操作比较:类型相同时,比较参数大小,写入上一步操作的参数中;类型不相同时,写入 T o p Top Top指向的数组位置。当 T o p Top Top大于等于 2 2 2时,需要向上合并,与 T o p − 2 Top-2 Top2项的参数作比较,考虑本次输入是否使得上两步操作无效,无效时本次操作写入到 T o p − 2 Top-2 Top2项位置中,直到已经输入的操作均有效时,进行下一步输入操作,本次操作输入到 T o p − 2 Top-2 Top2项位置中。
  <4>、当结构体数组中存 储的都是有效操作时。可以发现,操作类型 1 1 1 0 0 0交替,参数在类型为 1 1 1时递增,参数在类型为 0 0 0时递减。而且首次操作一定时 0 0 0,因为初始序列时操作为 1 1 1
  <5>、输入的操作数据完成后,可以完成排序任务了。参数在类型为 1 1 1时递增,参数在类型为 0 0 0时递减,说明每一次真正需要排序的序列区间在不断的缩小,为 0 0 0时改变右端点,为 1 1 1时改变左端点。又类型 1 1 1 0 0 0交替,说明本次排序在上一次排序结果后,反转区间数据顺序即可。

#include<stdio.h>
#define SIZE 100005
int MAX(int,int);
int MIN(int,int);
void Reserse(int *,int,int);
struct Operation{
	int O_Type;
	int O_Seat;
}O[SIZE];
int arry[SIZE];
int main()
{
	int i,j,top = 1;
	int Length,Times,Type,Seat;
	scanf("%d %d",&Length,&Times);
	for(i=1;i<=Length;i++){ arry[i] = i; }  //初始化序列 
	O[0].O_Type = 1, O[0].O_Seat = 1; 
	for(i=1;i<=Times;i++){
		scanf("%d %d",&Type,&Seat);
		//同类型比较参数
		if( Type == O[top-1].O_Type ){	
			if(Type == 1){
				O[top-1].O_Seat = MIN(O[top-1].O_Seat , Seat);  //升序情况下寻找最小的位置 
			}
			if(Type == 0){
				O[top-1].O_Seat = MAX(O[top-1].O_Seat , Seat); //降序情况下寻找最大的位置
			}
			top--;
		}
		else{
			O[top].O_Type = Type, O[top].O_Seat = Seat;
		} 
		//向上合并
		while(top-2 >= 0){
			if((Type == 0 && Seat >= O[top-2].O_Seat) || (Type == 1 && Seat <= O[top-2].O_Seat)){
				O[top-2].O_Seat = Seat;
				top = top - 2;
			}
			else{
				break;
			}
		}
		top++;  
	}
	//定区间
	int Left = 1, Right = Length;
	for(i=1;i<top;i++){	
		O[i].O_Type == 0  ? (Right = O[i].O_Seat) : (Left = O[i].O_Seat) ;		
		Reserse(arry,Left,Right);
	}
	for(i=1;i<=Length;i++){
		printf("%d ",arry[i]);
	}
	return 0;
} 
 
int MAX(int Value1,int Value2){ return Value1 > Value2 ? Value1 : Value2;}
int MIN(int Value1,int Value2){ return Value1 < Value2 ? Value1 : Value2;}
void Reserse(int a[],int L,int R){
	while( L < R){
		int temp = arry[L];
		arry[L] = arry[R];
		arry[R] = temp;
		L++; R--; 
	}
}

2、优化程序
  <1>根据数学常识,自然数 0 0 0 ~ n n n是奇偶数交替的,用奇偶性质便可以替代类型 0 0 0 1 1 1,那么就可以省去结构体。设定初始时的时候操作为 O p e r [ 0 ] = 1 Oper[0]=1 Oper[0]=1注意:这里偶数表示升,奇数表示降。
  <2>在输入操作数据时,首先判断类型
   (1). 当 t o p top top指向了偶数,那么输入了 0 0 0类型操作,就与上一步相同了,需比较大小,参数填入上一步数组中;那么输入了 1 1 1类型操作,就要循环与 T o p − 2 Top-2 Top2 步操作参数作比较,输入的参数大于等于 T o p − 2 Top-2 Top2 步操作参数,则上两步参数无效, T o p Top Top 退两步。
   (2). 同理,当 t o p top top指向了奇数,那么输入了 1 1 1类型操作,就与上一步相同了,需比较大小,参数填入上一步;那么输入了 0 0 0类型操作,就要循环与 T o p − 2 Top-2 Top2 步操作参数作比较,输入参数小于等于 T o p − 2 Top-2 Top2 步操作参数,则上两步参数无效, T o p Top Top 退两步。
  <3>上一套代码知道,排序的区间在不断地向中间缩小,那么非区间范围,一定在每一次操作后,都有被固定的数值。
   (1). 当操作为 0 0 0类型时,是将序列从 A r r y 1 Arry_1 Arry1 A r r y O p e r [ i ] Arry_{Oper[i]} ArryOper[i] 降序排列,那么从 A r r y O p e r [ i ] Arry_{Oper[i]} ArryOper[i] A r r y n Arry_n Arryn 数据就会被固定下来,后续也没有有效操作对其有影响。
   (2). 当操作为 1 1 1类型时,是将序列从 A r r y O p e r [ i ] Arry_{Oper[i]} ArryOper[i] A r r y n Arry_n Arryn 升序排列,那么从 A r r y 1 Arry_1 Arry1 A r r y O p e r [ i ] Arry_{Oper[i]} ArryOper[i] 数据就会被固定下来,后续也没有有效操作对其有影响。
   (3). 利用左右指针,分别向中间靠拢。为序列填入数据 V a l u e Value Value,填入的个数与每一步操作的参数有关, V a l u e Value Value从序列长度开始递减。
   (4). 完整数据输入完成后,当 t o p top top 为偶数时,操作为奇数个,最后一步操作为降序,指针应该从从 L e f t Left Left 一位一位逐步指向 R i g h t Right Right ,用来填入数据;当 t o p top top 为奇数时,操作为偶数个,最后一步操作为升序,指针应该从 R i g h t Right Right 一位一位逐步指向 L e f t Left Left,用来填入数据。

#include<stdio.h>
#define SIZE 100005
int MAX(int,int);
int MIN(int,int);
int Oper[SIZE];  //偶数操作为1 奇数的操作为0  值为位置 
int arry[SIZE];
int main()
{
	int i,top = 1;
	int Length,Times,Type,Seat;
	scanf("%d %d",&Length,&Times);
	Oper[0] =  1;
	while(Times--){
		scanf("%d %d",&Type,&Seat);
		if(Type){
			if(top%2 == 1){
				Seat = MIN(Oper[top-1],Seat);
				top--;
			}
			while(top-2 >= 0 && Seat <= Oper[top-2]){ top -= 2;} 
		}
		else{
			if(top%2 == 0){
				Seat = MAX(Oper[top-1],Seat);
				top--;
			}
			while(top-2 >= 0 && Seat >= Oper[top-2]){ top -= 2;}
		}
		Oper[top++] = Seat; 
	}
	int Left = 1, Right = Length;	 
	int Value = Length;
	for(i=1;i<top;i++){	
		if(i%2 == 1){
			while(Right > Oper[i] && Left <= Right){ 
				arry[Right--] = Value--;
			}
		}else{
			while(Left < Oper[i] && Left <= Right){
				arry[Left++] = Value--;
			} 
		}
		if(Left > Right) { break; }
	}
	if(top%2){
		while(Left <= Right){
			arry[Right--] = Value--;
		} 
	}else{
		while(Left <= Right){
			arry[Left++] = Value--;
		} 
	}
	for(i=1;i<=Length;i++)
		printf("%d ",arry[i]);
	return 0;
} 
 
int MAX(int Value1,int Value2){ return Value1 > Value2 ? Value1 : Value2;}
int MIN(int Value1,int Value2){ return Value1 < Value2 ? Value1 : Value2;}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值