zoukankan      html  css  js  c++  java
  • POJ2299Ultra-QuickSort (线段树和归并排序的解法)

    题目大意就是说帮你给一些(n个)乱序的数让你求冒泡排序需要交换数的次数(n<=500000)

    此题最初真不会做,我也只是在听了章爷的讲解后才慢慢明白过来的

    首先介绍线段树的解法:

    我们先将原数组每个值附上一个序号index,再将它排序。如题目的例子:

    num:   9  1  0  5  4

    index:  1  2  3  4  5

    排序后:

    num:   0  1  4  5  9

    index:  3  2  5  4  1

    然后由于排序后num为0的点排在原来数组的第3个,所以为了将它排到第一个去,那就至少需要向前移动两次,同时它也等价于最小的数0之前有2个数比它大(所以要移动两次),将0移到它自己的位置后,我们将0删掉(目的是为了不对后面产生影响)。再看第二大的数1,它出现在原数组的第二个,他之前有一个数比它大所以需要移动一次。这样一直循环下去那么着5个数所需要移动的次数就是:

    num:  0  1  4  5  9

    次数      2  1  2  1  0

    将次数全部要加起来就是最后所需要移动的总次数。

    方法章爷是已经告诉我了,但是最初我一直是觉得不好实现。到后来才慢慢、慢慢弄好。方法就是在建一棵树时,不是直接将原来的num放进树里面,而是将它的下标放进树里面,最初每个节点上赋值为1.然后当查找第一个num时,由于是找的下标为3的位置,所以我们就直接找区间[1,3)之间有多少个1(就是求前导和),这里面1的个数就是第一个num=0索要移动的次数,然后我们把0去掉,其实也就是吧下标为3的那个1去掉。这样每个值就依次计算出来了。

    当然其实只要是想明白了,不用线段树,直接用树状数组写起来会简便很多。(因为每次只需要计算前导和以及去掉某一个点,是对点的操作)。

    这里再讲一下归并排序的方法(对于最基础就没有掌握好的我来说听到他们说归并排序可以解题时,我竟然一团雾水,竟然连归并排序都忘记了),看了一下归并排序的实现过程,其实马上就可以找到思路,由于本题实际上就是要求逆序对(即满足i<j,a[i]>a[j]的数对)的个数。而我们再回顾一下归并排序的过程:

    假设回溯到某一步,后面的两部分已经排好序(就是说当前需要归并的两个部分都是分别有序的),假设这两个序列为

    序列a1:2 3 5 9  

    序列a2:1 4 6 8

    此时我们的目的就是要将a1和a2合并为一个序列。

    由于在没排序前a2序列一定全部都是在a1序列之后的,当我们比较a2的1与a1的2时,发现1<2按照归并的思想就会先记录下a2的1,而这里实际上就是对冒泡排序的优化,冒泡是将a2的1依次与a1的9,5,3,2交换就需要4次,而归并却只有一次就完成了,要怎么去记录这个4呢,实际上由于1比2小而2后面还有4个数,也就是说那我的结果就必须要+4,也就是记录a1序列找到第一个比a2某一个大的数,他后面还余下的数的个数就是要交换的次数。

    同时我们看a2的4时,a1中第一个比它大的数是5,5之后共有两个数,那结果就+2,。依次下去就可以计算出结果。但是由于我们任然没有改变归并排序的过程。所以复杂度还是O(nlogn),比上面的线段树要快。

    另外,此题有一坑就是结果会超int32,,用__int64

    首先是线段树的解法11892 KB   1047 ms

     1 #include <stdio.h>
     2 #include <string.h>
     3 #include <algorithm>
     4 #define mem(a) memset(a,0,sizeof(a))
     5 #define MIN(a , b) ((a) < (b) ? (a) : (b))
     6 #define MAXN 500010
     7 #define INF  1000000007
     8 #define lson k<<1,     l,     mid
     9 #define rson (k<<1)|1, mid+1,  r
    10 
    11 using namespace std;
    12 
    13 int Tree[MAXN<<2], index[MAXN], num[MAXN];
    14 int N;
    15 
    16 int cmp(const int i, const int j)
    17 {
    18     return num[i] < num[j];
    19 }
    20 
    21 void Edit(int k, int l, int r, int num, int value)
    22 {
    23     Tree[k] += value;
    24     if(r==l) return ;
    25     int mid = (l+r) >> 1;
    26     if(num <= mid) Edit(lson, num, value);
    27     else Edit(rson, num, value);
    28 }
    29 
    30 int Search(int k, int l, int r, int L, int R)//L,R是要找的区间
    31 {
    32     if(L<=l && r<=R) return Tree[k];
    33     int mid = (l+r) >> 1;
    34     int ans = 0;
    35     if(L <= mid) ans += Search(lson, L, R);
    36     if(R > mid) ans += Search(rson, L, R);
    37     return ans;
    38 }
    39 
    40 int main()
    41 {
    42     while(~scanf("%d", &N) && N)
    43     {
    44         mem(Tree); mem(num); mem(index);
    45         for(int i=1;i<=N;i++)
    46         {
    47             scanf("%d", &num[i]);
    48             Edit(1, 1, N, i, 1);
    49             index[i] = i;
    50         }
    51         sort(index+1, index+N+1,cmp);
    52         long long ans = 0;
    53         for(int i=1;i<=N;i++)
    54         {
    55             ans += (Search(1, 1, N, 1, index[i])-1);
    56             Edit(1, 1, N, index[i], -1);
    57         }
    58         printf("%I64d
    ", ans);
    59     }
    60     return 0;
    61 }

    然后是归并排序(只有注释位置有改动,其他的无变化)4076K   438MS

     1 #include <stdio.h>
     2 #include <string.h>
     3 #define mem(a) memset(a, 0, sizeof(a))
     4 
     5 int N, A[500010], T[500010];
     6 __int64 ans;
     7 
     8 void Merg_Sort(int x,int y)
     9 {
    10     if(y-x<=1) return ;
    11     int mid = x + (y-x)/2;
    12     Merg_Sort(x,mid);
    13     Merg_Sort(mid,y);
    14     int p = x, q = mid, i=x;
    15     while(p<mid || q<y)
    16     {
    17         if(q>=y || (p<mid && A[p] <= A[q])) T[i++] = A[p++];
    18         else//else的条件是(p==mid || A[q] < A[p])
    19         {
    20             if(p<mid) ans+=(mid-p);//由于是p<mid,所以此时也就是相当于 A[q] < A[p]
    21             T[i++] = A[q++];       //上面同时A[p]是第一个<A[q]的数,所以+后面还有的数(mid-p)
    22         }
    23     }
    24     for(i=x;i<y;i++)
    25     {
    26         A[i] = T[i];
    27     }
    28 }
    29 
    30 int main()
    31 {
    32     while(~scanf("%d", &N) && N)
    33     {
    34         mem(A); mem(T);
    35         for(int i=0;i<N;i++)
    36         {
    37             scanf("%d", &A[i]);
    38         }
    39         ans = 0;
    40         Merg_Sort(0,N);
    41         printf("%I64d
    ",ans);//结果会超int32
    42     }
    43     return 0;
    44 }
  • 相关阅读:
    linux 更换golang版本
    ubuntu 搭建NFS
    golang 异步并发http轮询(爬虫)
    Mysql 事务锁等待时间超时
    排序算法之鸡尾酒排序
    Sql Server一个表向另一个表添加多条数据,关联时查询出一条数据
    Easyui datagrid 开始时间不能大于结束时间
    用python爬了上千万条招聘信息后,最终分析出python要学这些才能就业...
    用python把B站小姐姐跳舞视频爬下来,并打包成可以直接运行的exe文件
    女朋友股票亏惨了,我一怒之下用Python爬取了证券最新数据...
  • 原文地址:https://www.cnblogs.com/gj-Acit/p/3250525.html
Copyright © 2011-2022 走看看