归并排序

求逆序对

本着别人听懂了自己才算会的原则,今天就在这里丢人现眼一回(灬ꈍ ꈍ灬)

题意要求找出所有的逆序对。

首先就要明确两点:

  1. 仅仅对其进行排序然后记录执行次数是于事无补的。
  2. 暴力数的话时间复杂度是一定通不过的。

解决方案:

  • 将注意力聚焦到归并排序上(也可参考 排序系列 )。
  • 归并排序的特点==>先将整个数列拆成小块,然后排序合并。
  • 分析一种情况,如果“左块”的某个值大于“右块”的某个值,那么可以确定“左块”该值右边的所有值都大于“右块”的这个值(因为“左右两块”都是有序的,这里假定为正序(由小到大))。那么“左块”的这个值及其右边的所有值都与“右块”的这个值构成逆序对。写在程序中就是ans+=mid-x+1;
  • 每一次归并排序第一个循环结构至少会收容下完整的“一块”数列。如果“左块”被完全收容,说明“右块”右边有几个值是两块中最大的,不满足逆序对。如果“右块”被完全收容,说明“左块”右边有几个值是两块中最大的,但是由于“右块”被完全收容,所以满足条件的逆序对都已经添加了。
  • 所以每一次归并排序其实是得到满足“右块”所有值的逆序对。上面分析了不论是“左收容”还是“右收容”都能达到这个要求。
  • 形象来说,小块排序合并时是处理小块逆序对,大块排序合并时是处理大块逆序对。所以对于每个值考虑了它的所有逆序对(由近及远),用一种巧妙的方式(哦,顺便还排个序!)。

好吧!我摊牌了,我讲不清楚!o(╥﹏╥)o

注意点:

  • 排序本身时间复杂度不会崩,数组长度不会崩,但ans用int是一定会崩的。

具体代码如下:

 1 #include<iostream>
 2 #include<cstring>
 3 using namespace std;
 4 
 5 const int N=1e5+5;
 6 int a[N];
 7 long long ans;
 8 //归并排序
 9 void mergeSort(int begin,int end){
10     //递归终止条件
11     if(begin==end)return;
12 
13     int mid=(begin+end)/2,t[N];
14     mergeSort(begin,mid);
15     mergeSort(mid+1,end);
16     int x=begin,y=mid+1,r=begin; 
17     while(x<=mid&&y<=end){
18         if(a[x]>a[y]){
19             //核心算法
20             ans+=mid-x+1;
21             t[r++]=a[y++];
22         }else{
23             t[r++]=a[x++];
24         }
25     }
26     while(x<=mid){
27         t[r++]=a[x++];
28     }
29     while(y<=end){
30         t[r++]=a[y++];
31     }
32     //复制数组
33     while(--r>=begin){
34         a[r]=t[r];
35     }
36 }
37 int main(){
38     cin>>a[0];
39     for(int i=1;i<=a[0];i++)cin>>a[i];
40     mergeSort(1,a[0]);
41     cout<<ans;
42     return 0;
43 }

 

posted @ 2021-08-01 21:29  Rekord  阅读(65)  评论(0编辑  收藏  举报