Luogu P1327 数列排序

来源:互联网 发布:淘宝店铺开通直播 编辑:程序博客网 时间:2024/06/15 09:52

Luogu 1372 数列排序

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

题目描述给定一个数列{an},这个数列满足ai≠aj(i≠j),现在要求你把这个数列从小到大排序,每次允许你交换其中任意一对数,请问最少需要几次交换?输入输出格式输入格式:第一行,正整数n (n<=100,000)。以下若干行,一共n个数,用空格分隔开,表示数列{an},任意-231<ai<231。输出格式:只有一行,包含一个数,表示最少的交换次数。输入输出样例输入样例#1:88 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
原创粉丝点击