zoukankan      html  css  js  c++  java
  • 32.Sort

    Description

    众所周知,排序方法有许多种。例如:简单易懂的冒泡排序,平均复杂度较优的快速排序,以及不基于比较的基数排序等等。

    现在,小 DD 得到了一个自然数序列 {a1,a2,,an}{a1,a2,⋯,an}。他想要对其按照从小到大的顺序进行排序(即使得每个元素均严格不大于他的后继元素)。但由于基于比较的排序算法复杂度下界已经被证明为 Θ(nlog2n)Θ(nlog2⁡n),所以小 DD 决定尝试一种全新的排序算法:翻转排序

    在翻转排序中,小 DD 在每次操作中,可以选择一个区间 [l,r][l,r] (1lrn)(1≤l≤r≤n),并翻转 al,al+1,,aral,al+1,⋯,ar。即,在该次操作完后,序列将会变为 a1,a2,,al1,ar,ar1,,al+1,al,ar+1,ar+2,,ana1,a2,⋯,al−1,ar,ar−1,⋯,al+1,al,ar+1,ar+2,⋯,an。

    例如,对于序列 [1,6,2,4,3,5][1,6,2,4,3,5],若选择区间 [2,4][2,4] 进行翻转,则将会得到 [1,4,2,6,3,5][1,4,2,6,3,5]。

    定义一次操作的代价为 rl+1r−l+1,即翻转的区间长度。定义一个操作序列的代价为每次操作的代价之和。现在,请你帮助小 DD 求出一个代价足够小的操作序列(你并不一定要求出代价最小的操作序列)。

    Input

    第一行一个正整数 nn,表示序列长度。

    第二行 nn 个空格隔开的非负整数,表示小 DD 得到的自然数序列 a1,a2,,ana1,a2,⋯,an。

    Output

    输出若干行,每行两个空格隔开的正整数 l,rl,r (1lrn)(1≤l≤r≤n),表示一次翻转区间 [l,r][l,r] 的操作。

    最后输出一行 -1 -1,标志着操作序列的结束。

    Sample Input

    4 1 3 2 4

    Sample Output

    2 3 -1 -1

    Hint

    1.下发文件中提供了 checker.cpp,该程序将对于其所在目录下的 sort.in,判断其所在目录下的 sort.out 是否为一个正确的操作序列。若正确,将给出该操作序列的代价。若不正确,将给出错误信息。选手可以借助该程序来更好地检查自己的程序是否正确。

    运行时,必须保证 sort.in 为一个合法的输入,且需保证 sort.out 符合题目中描述的输出格式,否则出现任何结果均有可能。

    2.对于所有测试数据,保证 1n500001≤n≤50000,且 0ai1090≤ai≤109。

    (附checker.cpp)

     1 #include <algorithm>
     2 #include <cstdio>
     3 int arr[50005];
     4 int main()
     5 {
     6     FILE *fin = fopen("sort.in", "r"), *fout = fopen("sort.out", "r");
     7     if (!fin)
     8     {
     9         puts("INVALID : File sort.in not found.");
    10         return -1;
    11     }
    12     if (!fout)
    13     {
    14         puts("INVALID : File sort.out not found.");
    15         return -1;
    16     }
    17     int n;
    18     fscanf(fin, "%d", &n);
    19     for (int i = 0; i < n; i++)
    20         fscanf(fin, "%d", arr + i);
    21     int l, r, sum = 0;
    22     while (~fscanf(fout, "%d%d", &l, &r))
    23     {
    24         if (l == -1 && r == -1)
    25             break;
    26         sum += r - l + 1;
    27         if (l <= 0 || l > n)
    28         {
    29             printf("INVALID : l = %d is not in range [1, %d].
    ", l, n);
    30             return -1;
    31         }
    32         if (r <= 0 || r > n)
    33         {
    34             printf("INVALID : r = %d is not in range [1, %d].
    ", r, n);
    35             return -1;
    36         }
    37         if (l > r)
    38         {
    39             printf("INVALID : %d = l > r = %d.
    ", l, r);
    40             return -1;
    41         }
    42         if (sum > 20000000)
    43         {
    44             puts("INVALID : Too much cost.");
    45             return -1;
    46         }
    47         std::reverse(arr + --l, arr + r);
    48     }
    49     bool f = true;
    50     for (int i = 1; i < n; i++)
    51         f &= arr[i] >= arr[i - 1];
    52     if (!f)
    53     {
    54         puts("INVALID : Not sorted.");
    55         return -1;
    56     }
    57     printf("VALID : Total cost is %d.
    ", sum);
    58     return 0;
    59 }
    checker

    Solution

    本题算是一个比较新颖的题目,实际上是用这种翻转来模拟实现归并排序:

    先将给定数列进行离散化,每次选定一个中间的数,将小于等于它的排在左边,大于它的排在右边,再依次递归两边就可以了;

    主要是复杂度(进行操作的次数)证明a....    排一遍要n,由于其非01性,所以进行log2n次的n排,故排一遍将l到r以stdd为标准换为左边小于等于stdd,右边大于等于stdd的操作数为Θ(nlog2n) ;

    T(n) = 2T(n/2) + Θ(nlog2n),可以解得 T(n) = Θ(nlog22n) ;
    上代码。

     1 #include<bits/stdc++.h>
     2 #define maxn 50005
     3 using namespace std;
     4 int n,pos[maxn];
     5 struct node{
     6     int v,id;
     7     friend bool operator<(node a,node b){
     8         return a.v<b.v;
     9     }
    10 }nd[maxn];
    11 int stdd;
    12 void run(int l,int r){
    13     if(l==r) return;
    14     int mid=l+r>>1;
    15     run(l,mid);run(mid+1,r);
    16     
    17     int L,R;//要翻转的区间 
    18     L=R=mid;//L和R的初值 
    19     int i=mid,j=mid+1;
    20     while(i>=l&&pos[i]>stdd) L=i--;
    21     while(j<=r&&pos[j]<=stdd) R=j++;
    22     if(L!=R){
    23         printf("%d %d
    ",L,R);
    24         reverse(pos+L,pos+R+1);
    25     } 
    26 } 
    27 void msort(int l,int r){
    28     if(l==r) return;
    29     
    30     int mid=stdd=(l+r)>>1;
    31     run(l,r);//将l到r以stdd为标准换为左边小于等于stdd,右边大于等于stdd
    32  
    33     msort(l,mid);msort(mid+1,r);    
    34 }
    35 void init(){
    36     scanf("%d",&n);
    37     for(int i=1;i<=n;i++){
    38         scanf("%d",&nd[i].v);nd[i].id=i;
    39     }
    40     sort(nd+1,nd+n+1);
    41     for(int i=1;i<=n;i++) pos[nd[i].id]=i;//离散化位置确定 
    42     
    43     msort(1,n);
    44     printf("-1 -1
    ");
    45 }
    46 int main(){
    47     //freopen("sort.in","r",stdin);
    48     //freopen("sort.out","w",stdout);
    49     init();
    50  
    51     return 0;
    52 }
  • 相关阅读:
    O(1)时间求出栈内元素最小值 小强斋
    图片的轮廓 小强斋
    png 图片的缩放 小强斋
    Dom4j 小强斋
    O(1)时间求出栈内元素最小值 小强斋
    图片的轮廓 小强斋
    png图片的读取 小强斋
    字符串的最大重复数 小强斋
    【Android】数据的四种存储方式
    【就业】签offer和签三方协议的不同
  • 原文地址:https://www.cnblogs.com/degage/p/9684682.html
Copyright © 2011-2022 走看看