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 }
原文地址:https://www.cnblogs.com/degage/p/9684682.html