Luogu P1327 数列排序

Luogu 1372 数列排序

上午学校搞模拟赛的第一题——其实真的是够了……快排+二分能过,不过题解给的快排+数学方法看起来也不错,这里只说说我的做法:

题目描述
给定一个数列{an},这个数列满足ai≠aj(i≠j),现在要求你把这个数列从小到大排序,每次允许你交换其中任意一对数,请问最少需要几次交换?
输入输出格式
输入格式:
第一行,正整数n (n<=100,000)。
以下若干行,一共n个数,用空格分隔开,表示数列{an},任意-231<ai<231。

输出格式:
只有一行,包含一个数,表示最少的交换次数。

输入输出样例
输入样例#1:
8
8 23 4 16 77 -5 53 100
输出样例#1:
5

首先看题第一反应就是选择排序——为啥呢?除了桶排这个不需要交换的排序算法以外,选择排序是执行交换次数最少的排序,注意这里是交换顺序不是时间复杂度……
紧接着——有一个定理——用选择排序排序时交换次数就是最少的,也就是我们的输出,然而我觉着这个并不能证明……那么问题来了,选择排序时间复杂度为O(n^2)很明显100000的数据是妥妥的炸了,于是选择排序废了
继续下一个思路——还记得树状数组逆序对么?或者说还记得归并排序逆序对么?然而经过试验——或者是手动笔算,你会发现逆序对根本就是WA——也就是说最小交换次数又不是逆序对个数
没办法我们只好来自行模拟选择排序的过程……其实只是大概借鉴了一下选排的思路——首先我们Sort一遍把序列排好,当然要保存原来的序列,这里我用了r(其实是一开始用归并排序剩下来的r数组)
然后呢?按照选排的方法卡就行了——首先我们找到r[1],然后在排好序的a中找到对应的元素——假设是第k个元素——把r[1]与r[k]交换过来,cnt++——接着继续找r[2]……这时可以轻易地发现——a中元素单调递增,满足二分查找的性质,于是我们来写个二分查找解决查找k的步骤——总时间复杂度O(n log n)
然而通过模拟样例可以发现一个漏洞——就是我们只搜一遍还不行,因为r数组有可能根本没排序完成,于是我们来第二遍、第三遍,反正只要r排好序了就行,相当于我们完成了选排的任务,这时候写个While来判断r是否有序就好啦,别忘了判断是否是第一个first,具体看代码自己看一下就好啦。
下面贴上AC代码——

#include <cstdio>
#include <algorithm>
using namespace std;
int a[300000],r[300000],n,t,i,cnt,k;
bool b=false,first;
int find(int num)
{
    int l=1,r=n,mid;
    while (l!=r)
    {
        mid=(l+r)>>1;
        if (a[mid]>=num) r=mid;
        else l=mid+1;
    }
    return l;
}
int main()
{
    freopen("seqsort.in","r",stdin);
    freopen("seqsort.out","w",stdout);
    scanf("%d",&n);
    for (i=1;i<=n;i++) 
    {
        scanf("%d",&a[i]);
        r[i]=a[i];
    }
    sort(a+1,a+n+1);
    while (!b)
    {
        first=true;
        for (i=1;i<=n;i++)
        {
            k=find(r[i]);
            if (k!=i)
            {
                b=false;
                t=r[i]; r[i]=r[k]; r[k]=t;
                cnt++;
            }
            else 
                if (first) { b=true; first=false; }
                else b=b&&true;
        }
    }
    printf("%d",cnt);
    fclose(stdin); fclose(stdout);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值