• 休息【归并排序】【模拟】


    题目大意:

    给出一个数列,每次可以把一个单调递减的序列反过来,求最少要反多少次才能使这个序列单调递增。
    Input

    6
    5 3 2 1 6 4

    Output

    3

    思路:

    对于一个数列,我们可以先按照题目所说的将单调递减的数列取反。

    • 5 3 2 1 6 4

    就变成

    • 1 2 3 5 4 6

    这时候,每个曾经单调递减的数列就单调递增了,每个区间内的数列肯定满足单调递增,所以剩下的肯定是两个区间之间的数字不满足单调递增。
    那么很容易发现,剩余需要取反的数字个数一定是现在数列逆序对的个数,所以,再用归并排序求出逆序对的个数即可。


    代码:

    #include <cstdio>
    #include <iostream>
    using namespace std;
    
    long long n,a[100011],b[100011],sum,i,j;
    
    void make(long long l,long long mid,long long r)  //合并
    {
        long long p1=l,p2=mid+1;
        for (long long i=l;i<=r;i++)
        {
            if (p1>mid) {b[i]=a[p2]; p2++;}
            else if (p2>r) {b[i]=a[p1]; p1++;}
            else if (a[p1]>a[p2]){sum+=1+r-p2; b[i]=a[p1]; p1++;}
            else {b[i]=a[p2]; p2++;}
        }
        for (long long i=l;i<=r;i++)  //重新赋值
         a[i]=b[i];
    }
    
    void sorts(long long l,long long r)  //拆分
    {
        if (l<r)
        {
            long long mid=(l+r)/2;
            sorts(l,mid);
            sorts(mid+1,r);  //拆成两半
            make(l,mid,r);
        }
    }
    
    int main()
    {
        scanf("%lld",&n);
        for (i=1;i<=n;i++)
         scanf("%lld",&a[i]);
        i=1;
        j=1;
        while (i<n)  //先进行一次
        {
            while (a[j]>a[j+1]&&j<n) j++;
            if (j-i)
            {
                for (long long k=i;k<=i+(j-i+1)/2-1;k++)
                 swap(a[k],a[j-k+i]);
                sum++;
            }
            i=j=j+1;
        }
        sorts(1,n);  //求逆序对
        printf("%lld\n",sum);
        return 0;
    }
  • 相关阅读:
    面向对象---2
    面向对象---1
    数组的复制、反转、查找(线性查找,二分法查找)
    Oracle 开放端口供客户机连接
    ORACLE常用函数大全
    ORACLE常用脚本
    C#开发实用知识点总结
    线程对话框基类
    C#开发常见问题处理
    通过修改注册表实现IE设置
  • 原文地址:https://www.cnblogs.com/hello-tomorrow/p/11998843.html
Copyright © 2020-2023  润新知