zoukankan      html  css  js  c++  java
  • Noi.ac #32题解

    题目链接

    题面:

    (Description)

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

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

    在翻转排序中,小 (D) 在每次操作中,可以选择一个区间 ([l,r] (1≤l≤r≤n)),并翻转 (a_l,a_{l+1},⋯,a_r)。即,在该次操作完后,序列将会变为 (a_1,a_2,⋯,a_{l−1},a_r,a{r−1},⋯,a_{l+1},a_l,a_{r+1},a_{r+2},⋯,a_n)

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

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

    (Input)

    第一行一个正整数(n),表示序列长度。

    第二行(n)个空格隔开的非负整数,表示小(D)得到的自然数序列({{a_i}})

    (Output)

    输出若干行,每行两个空格隔开的正整数 (l,r) ((1≤l≤r≤n)),表示一次翻转区间([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) 符合题目中描述的输出格式,否则出现任何结果均有可能。

    (checker.cpp:)

    #include <algorithm>
    #include <cstdio>
    int arr[50005];
    int main()
    {
        FILE *fin = fopen("sort.in", "r"), *fout = fopen("sort.out", "r");
        if (!fin)
        {
            puts("INVALID : File sort.in not found.");
            return -1;
        }
        if (!fout)
        {
            puts("INVALID : File sort.out not found.");
            return -1;
        }
        int n;
        fscanf(fin, "%d", &n);
        for (int i = 0; i < n; i++)
            fscanf(fin, "%d", arr + i);
        int l, r, sum = 0;
        while (~fscanf(fout, "%d%d", &l, &r))
        {
            if (l == -1 && r == -1)
                break;
            sum += r - l + 1;
            if (l <= 0 || l > n)
            {
                printf("INVALID : l = %d is not in range [1, %d].
    ", l, n);
                return -1;
            }
            if (r <= 0 || r > n)
            {
                printf("INVALID : r = %d is not in range [1, %d].
    ", r, n);
                return -1;
            }
            if (l > r)
            {
                printf("INVALID : %d = l > r = %d.
    ", l, r);
                return -1;
            }
            if (sum > 20000000)
            {
                puts("INVALID : Too much cost.");
                return -1;
            }
            std::reverse(arr + --l, arr + r);
        }
        bool f = true;
        for (int i = 1; i < n; i++)
            f &= arr[i] >= arr[i - 1];
        if (!f)
        {
            puts("INVALID : Not sorted.");
            return -1;
        }
        printf("VALID : Total cost is %d.
    ", sum);
        return 0;
    }
    

    2.对于所有测试数据,保证 (1≤n≤50000),且 (0≤ai≤10^9)

    (Solution)

    这道题结合了快速排序和归并排序的思想。
    先考虑(01)串:
    对于(01)串,我们考虑类似归并排序的形式,分治下去,回溯时左半部分的右边均为(1),右半部分的左边均为(0),进行一次(Reverse)即可
    再考虑正解:
    我们可以类似快速排序,选择一个基准数(stdq),将满足(a_i leq stdq)(a_i)视为(0)(1)同理,再按(01)串的方法处理即可
    在代码实现的过程中,我们用离散化来实现,其中(pre[])用于维护大小关系,具体见代码

    (Code:)

    #include<bits/stdc++.h>
    using namespace std;
    namespace my_std
    {
    	typedef long long ll;
    	#define fr(i,x,y) for(int i=(x);i<=(y);i++)
    	#define pf printf
    	inline ll read()
    	{
    		ll sum=0,f=1;
    		char ch=0;
    		while(!isdigit(ch))
    		{
    			if(ch=='-') f=-1;
    			ch=getchar();
    		}
    		while(isdigit(ch))
    		{
    			sum=(sum<<1)+(sum<<3)+(ch^48);
    			ch=getchar();
    		}
    		return sum*f;
    	}
    	inline void write(int x)
    	{
    	    if(x<0)
    		{
    	    	putchar('-');
    			x=-x;
    		}
    	    if(x>9) write(x/10);
    	    putchar(x%10+'0');
    	}
    }
    using namespace my_std;
    const int N=5e4+50;
    int n,pre[N],stdq;
    struct qq
    {
    	int v,val; 
    }a[N];
    bool cmp(qq a,qq b)
    {
    	return a.v<b.v;
    }
    inline void work(int l,int r)
    {
    	if(l==r) return ;
    	int mid=(l+r)>>1;
    	work(l,mid),work(mid+1,r);
    	int lefty=mid,righty=mid,lp=mid,rp=mid+1;
    	while(lp>=l&&pre[lp]>stdq) lefty=lp--;
    	while(rp<=r&&pre[rp]<=stdq) righty=rp++;
    	if(lefty!=righty)
    	{
    		pf("%d %d
    ",lefty,righty);
    		reverse(pre+lefty,pre+righty+1); 
    	}
    }
    inline void mergesort(int l,int r)
    {
    	if(l==r) return;
    	int mid=stdq=(l+r)>>1;
    	work(l,r);
    	mergesort(l,mid),mergesort(mid+1,r);
    }
    int main(void)
    {
    	n=read();
    	fr(i,1,n) a[i].v=read(),a[i].val=i;
    	sort(a+1,a+n+1,cmp);
    	fr(i,1,n) pre[a[i].val]=i; 
    	mergesort(1,n);
    	puts("-1 -1");
    	return 0;
    }
    

    完结撒花!!

  • 相关阅读:
    CocoaPods 中删除不需要的第三方
    真机测试是提示 的 iPhone is busy: Preparing debugger support for
    关于 Expression is not assignable 错误
    关于查询ios的app更新的历史版本记录
    ios开启双重认证之填坑式教学
    swift 有道 翻译文档(2 条件语句 循环语句)
    swift 有道 翻译文档(1 定义变量常量,数组字典)
    渐变UI
    cornerstone 使用
    cocoaPods安装
  • 原文地址:https://www.cnblogs.com/lgj-lgj/p/12332542.html
Copyright © 2011-2022 走看看