「EZEC-10」排列排序

目录

题目描述

输入

输出

样例

输入数据 1

输出数据 1

提示

分析

Code


题目描述

小 E 给你一个长度为 n 的排列P_{1},P_{2},...P_{n-1},P_{n} 。小 E 想要把它排序。 

 小 E 每次可以花区间长度,即 r−l+1 的代价,选择排列中的任意一段区间 [l,r],并将 [l,r] 从小到大排序。

现在你可以让他进行若干次这个操作,直到 p 中元素的值从 1到 n 按升序排序,即对于 1 到 n 的每一个 i,都有 P_{i}=i

小 E 问你,他花的代价最少为多少?

输入

本题有多组询问,第一行有一个数 TT 表示询问组数。

对于每组询问:

第一行给出一个整数 n。

第二行 n 个整数,由空格隔开,代表排列 p 中元素的值。

输出

T行,每行一个整数表示一组询问的答案。

样例

输入数据 1

2
3
1 3 2
4
3 2 1 4

输出数据 1

2
3

提示

分析

        1)首先需要理解题目需要我们干什么,得到什么用的结果。

              随便举个例子:1,3,4,6,5,2,8,7

             第一种比较简单的方法就是直接选择区间【2,8】进行从小到大排序,通过付出代价8-2+1=7,就能实现从小到大的排序。但是这是不是最小的代价呢,我们再考察考察。题目要求的是最小的代价是多少?这明显也是个贪心问题。

             

         从上面数据表来看,我们发现p[1]=1,p[5]=5也就是这两个位置的数据已经对应上了,是不需要再排序了。而上面对【2,8】区间的排序,自然就把p[5]算上去了,把不必要排的也排了,所以不是最优解。

          第二种方法,分区间排序

          先对区间【2,4】进行从小到大排序,这样代价=4-2+1=3

             

          然后再对区间【6,8】进行从小到大排序,付出代价8-6+1=3

             

          最终总代价=3+3=6,小于第一种方法的代价7。

          但是对于下面这种情况又该怎么办呢

            

           这样的话,P[5]也是必须参与排序才能实现从小到大的排序。代价至少为7

     

 2)思路

        通过上面的实例分析,仔细观察可以发现就可以通过L,R游标(相当于指针)来游动。当P[i]!=i的时候,可以令L=i,然后令R=i+1,R++开始向右滑动。同时不断维护区间的最大值max=P[],直到R=max(因为要想对区间的数进行从小到大排序,那么一定是要考虑到最大值刚好能排在区间的顶部才行。)

        然后在将L从R+1,R++开始继续往下重复上述的滑动寻找。同时将每个区间的代价累加起来。

        例如:1 3 4 2 5 8 7 6

        区间[2,4]的最大值为4,而区间的上限刚好为4,所以可以对此区间进行排序。然后L转到4+1=5的位置开始继续寻找下一个区间。因为p[5]=5,所以p[5]不需要排序,L更新为6,继续寻找,刚好p[6]!=6,所以又开始新一个区间的左端点,R=L+1,R++继续滑动,最后[6,8]区间的最大值为8,而区间上限刚好也是8,所以可以对此区间进行从小到大排序。

       例如:1 3 4 8 5 2 7 6

       如果是这组数,我们发现左端点为L=2,右端点R=L+1,R++往右滑动,而区间的最大值为8,所以只有对区间[2,8]的数进行排序才有可能实现从小到大排序。最终代价为8-2+1=7

Code

#include <bits/stdc++.h>
using namespace std;
int a[1000005];

int main()
{
    int T;
    cin >> T;
    while (T--)
    {
        int n, ans = 0;
        scanf("%d", &n);
        for (int i = 1; i <= n; i++) scanf("%d", &a[i]);
        int L = 1;
        while (L <= n)
        {
            if (a[L] == L) // 不需要排序 
                L++;//更新左端点 
            else 
            {
                int max_num = a[L]; //区间最大值 
                int R = L + 1;
                max_num = max(max_num, a[R]); //维护区间最大值 
                while (max_num > R)
                {
                    R++;//右端点往右滑动 
                    max_num = max(max_num, a[R]);//维护区间最大值 
                }
                ans += R - L + 1; //累计代价 
                L = R + 1; //更新左端点 
            }
        }
        cout << ans << endl;
    }
    return 0;
}

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值