归并排序及应用 (nyoj 117 求逆序数)

求逆序数

时间限制:2000 ms  |  内存限制:65535 KB
难度:5
 
描述

在一个排列中,如果一对数的前后位置与大小顺序相反,即前面的数大于后面的数,那么它们就称为一个逆序。一个排列中逆序的总数就称为这个排列的逆序数。

现在,给你一个N个元素的序列,请你判断出它的逆序数是多少。

比如 1 3 2 的逆序数就是1。

 
输入
第一行输入一个整数T表示测试数据的组数(1<=T<=5)
每组测试数据的每一行是一个整数N表示数列中共有N个元素(2〈=N〈=1000000)
随后的一行共有N个整数Ai(0<=Ai<1000000000),表示数列中的所有元素。

数据保证在多组测试数据中,多于10万个数的测试数据最多只有一组。
输出
输出该数列的逆序数
样例输入
2
2
1 1
3
1 3 2
样例输出
0
1


归并排序执行过程:
          1、执行归并排序函数时,把全部的数字一分为二,继续递归调用函数自身,左一半右一半的划分开,直到每一份里只有一个元素为止,停止划分。
         2、把划分开的元素按照大小顺序排列,先 1 1,合并为个数为 2 的数组,再把 2 2 按顺序大小要求合并成个数为 4 的数组,依次进行把所有元素按大小排序
            两两合并时两序列均已是有序序列

如:
    4 1 3 10 7 3 5 0
    
    4 1 3 10 7 3 5 0

    4 1 3 10 7 3 5 0

    4 1 3 10 7 3 5 0 //每一组个数为 1 结束



合并:  1 4 3 10 3 7 0 5

      1 3 4 10 0 3 5 7

最后结果:   0 1 3 3 4 5 7 10




本题利用归并时比较大小条件,因为两序列合并时都已经是有序的,所以一旦遇到前一个序列的a比后一个序列中的一个值大,那么前一个序列数的值a以后的数(包括a)都比后序列的值大,所以sum += (p3-s1+1); sum用long long.

代码如下:
 1  
 2 //归并排序
 3 
 4 #include <stdio.h>
 5 #include <stdlib.h>
 6 long long sum = 0;
 7 void mergesort(int a[],int ,int );
 8 int main()
 9 {
10     int a[1000300];
11     int T,n,i;
12     scanf("%d",&T);
13     while(T--)
14     {
15         sum = 0;
16         scanf("%d",&n);
17         for(i=0;i<n;i++)
18             scanf("%d",&a[i]);
19         mergesort(a,0,n-1);
20 
21         printf("%lld\n",sum);
22     }
23     return 0;
24 }
25 
26 void merge(int a[],int s1,int e1,int s2,int e2)
27 {
28     //s1-e1为左数组
29     //s2-e2为右数组
30     int j = 0,i,p1 = s1,p2 = e2,p3 = e1;
31     int *temp = (int*)malloc(sizeof(int)*(e2-s1+5));
32 
33     while(s1 <= e1 && s2 <= e2)
34     {
35         if(a[s1] <= a[s2])
36         {
37             temp[j++] = a[s1++];continue;
38         }
39         else
40         {
41             sum += (p3-s1+1);
42             temp[j++] = a[s2++];continue;
43         }
44     }
45     while(s1 <= e1)//如果左边还有
46         temp[j++] = a[s1++];
47 
48     while(s2 <= e2)//如果右边还有
49         temp[j++] = a[s2++];
50 
51     j = 0;
52     for(i = p1;i <= p2;i++)//把排好序的数组存回原来的地方
53         a[i] = temp[j++];
54 
55     delete temp;//记得释放指针
56 }
57 void mergesort(int a[],int left,int right)
58 {
59     //先一直拆分左边,知道拆分的只剩下一个元素为止,然后以一个元素为对称,右边也只有一个元素,然后合并两个元素
60     //一直这样向上合并,先1 1合并,然后2 2 合并 4 4..8 8..16 16...知道所有元素都已经合并完成
61     //带合并的两列数字都已经是有序的
62     int mid;
63     if(left < right)
64     {
65         mid = (left+right)/2;
66         mergesort(a,left,mid);//左拆分
67         mergesort(a,mid+1,right);//右拆分
68 
69         merge(a,left,mid,mid+1,right);//合并
70     }
71 }
72         

 









posted @ 2016-09-20 14:32  961897  阅读(205)  评论(0编辑  收藏  举报