蓝桥杯-双向排序

文章描述了一位程序员在解决一个涉及到多次排序操作的问题时,从使用冒泡排序开始,逐步优化到快速排序,并通过自定义比较器进行降序排列。在遇到无效操作和操作范围问题后,程序员通过分析操作规律,提出了一种更高效的方法,通过构建内部类记录连续操作并优化执行顺序,最终成功实现了全部测试用例的通过。
摘要由CSDN通过智能技术生成

先暴力一下试试水

直接写了两个冒泡提交,然后发现...

是的,只过了三成的案例

是不是因为冒泡太长时间了,先吃个饭回来继续。。。

上快排+比较器

import java.util.Arrays;
import java.util.Comparator;
import java.util.Scanner;
class MyComparator implements Comparator<Integer>
{

    @Override
    public int compare(Integer o1, Integer o2) {
        // TODO Auto-generated method stub
        return o2-o1;//降序
    }
    
}
public class DoubleSort {
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        Scanner sc=new Scanner(System.in);
        int n=sc.nextInt();
        int m=sc.nextInt();
        Integer[] a=new Integer[n];
        for(int i=0;i<n;i++)
        {
            a[i]=i+1;
        }
        while(n>0)
        {
            int flag=sc.nextInt();
            int i=sc.nextInt();
            Comparator cp=new MyComparator();
            if(flag==0)
            {
                Arrays.sort(a, 0, i, cp);
            }
            else
            {
                Arrays.sort(a,i-1,a.length);
            }
            n--;
        }
        for(int i=0;i<a.length;i++)
        {
            System.out.print(a[i]+" ");
        }
    }
}

commit...

只过了六成,还是没能全过...

这时候发现操作范围问题 有相当一部分的操作是无效的

范围大的覆盖范围小的,优化一下操作,提取有效操作

//建一个内部类记录连续出现的操作记录
    public static class reco
    {
        int first;
        int second;
        reco(int a,int b)
        {
            first=a;//标记升降序
            second=b;//标记边界
        }
    }
int n,m,top;
        Scanner sc=new Scanner(System.in);
        n=sc.nextInt();
        m=sc.nextInt();
        int[] arr=new int[n+1];
        reco[] r=new reco[n+1];
        top=0;
        while(m!=0)
        {
            int p=sc.nextInt();
            int q=sc.nextInt();
            if(p==1&&top>0) {//升序
                while(top>0&&r[top].first==1) 
                {//top记录上一个操作
                 if(r[top].second<=q) //找最左边界
                  q=r[top].second;
                 --top;
                }
                while(top>=2&&r[top-1].second>=q) {
                 top-=2;//删除一组操作(即0操作和1操作)
                }
                r[++top]=new reco(1,q);//top始终停留在要记录操作的前一步
                }
               if(p==0){//降序
                while(top>0&&r[top].first==0) {
                 if(r[top].second>=q)//找最右边界
                  q=r[top].second;
                 --top;
                 }
                while(top>=2&&r[top-1].second<=q)
                 top-=2;
                r[++top]=new reco(0,q);      
                }
            m--;
        }

提取完成后到了关键一步,就是如何排序,继续用快排的话有点用牛刀了。

看了一下大佬们的思路(太妙了,要是考试我根本不会发现估计只会直接排序了吧)发现优化后的规律

  1. 第一个操作一定是0操作

  1. 0 1必然交替出现

  1. 由于只限制左右边界,发现每次操作完靠左右的数趋于固定,中间乱序,并逐渐向中间固定

int k=n;
        int left=1;
        int right=n;
          for(int i=1;i<=top;i++) {//先后依次执行有效操作
           if(r[i].first==0) {
            while(left<=right&&r[i].second<right)//右边界范围内的升序操作
             arr[right--]=k--;
           }
           if(r[i].first==1) {
            while(left<=right&&r[i].second>left)//左边界范围内的降序操作
             arr[left++]=k--;
           }
          }
          //因为操作1必然是0操作,所以数字没被固定完时奇数降序偶数升序,直到全部固定完毕
          if(top%2==0) {//升序
           while(left<=right)
            arr[right--]=k--;
          }
          else {//降序
           while(left<=right)
            arr[left++]=k--;
          }
          for(int i=1;i<=n;i++) {
           System.out.print(arr[i]+" ");
          }

赋上完整代码

import java.util.*;
public class DoubleSort2{
    //无效步骤判断
    //第一次一定是0操作
    //当前操作范围囊括前面同类型的,前面步骤就可删去
    //01交替依次往中间固定
    //连续出现,范围覆盖
    //建一个内部类记录连续出现的操作记录
    public static class reco
    {
        int first;
        int second;
        reco(int a,int b)
        {
            first=a;//标记升降序
            second=b;//标记边界
        }
    }
    public static void main(String[] args)
    {
        int n,m,top;
        Scanner sc=new Scanner(System.in);
        n=sc.nextInt();
        m=sc.nextInt();
        int[] arr=new int[n+1];
        reco[] r=new reco[n+1];
        top=0;
        while(m!=0)
        {
            int p=sc.nextInt();
            int q=sc.nextInt();
            if(p==1&&top>0) {//升序
                while(top>0&&r[top].first==1) 
                {//top记录上一个操作
                 if(r[top].second<=q) //找最左边界
                  q=r[top].second;
                 --top;
                }
                while(top>=2&&r[top-1].second>=q) {
                 top-=2;//删除一组操作(即0操作和1操作)
                }
                r[++top]=new reco(1,q);//top始终停留在要记录操作的前一步
                }
               if(p==0){//降序
                while(top>0&&r[top].first==0) {
                 if(r[top].second>=q)//找最右边界
                  q=r[top].second;
                 --top;
                 }
                while(top>=2&&r[top-1].second<=q)
                 top-=2;
                r[++top]=new reco(0,q);      
                }
            m--;
        }
        int k=n;
        int left=1;
        int right=n;
          for(int i=1;i<=top;i++) {//先后依次执行有效操作
           if(r[i].first==0) {
            while(left<=right&&r[i].second<right)//右边界范围内的升序操作
             arr[right--]=k--;
           }
           if(r[i].first==1) {
            while(left<=right&&r[i].second>left)//左边界范围内的降序操作
             arr[left++]=k--;
           }
          }
          //因为操作1必然是0操作,所以数字没被固定完时奇数降序偶数升序,直到全部固定完毕
          if(top%2==0) {//升序
           while(left<=right)
            arr[right--]=k--;
          }
          else {//降序
           while(left<=right)
            arr[left++]=k--;
          }
          for(int i=1;i<=n;i++) {
           System.out.print(arr[i]+" ");
          }
    }
}

提交一下看看

accepted

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值