Sweety

Practice makes perfect

导航

(分治思想)(归并排序)C - Ultra-QuickSort(7.2.2)

Posted on 2014-07-23 15:18  蓝空  阅读(153)  评论(0编辑  收藏  举报

 

 

Description

In this problem, you have to analyze a particular sorting algorithm. The algorithm processes a sequence of n distinct integers by swapping two adjacent sequence elements until the sequence is sorted in ascending order. For the input sequence
9 1 0 5 4 ,

Ultra-QuickSort produces the output
0 1 4 5 9 .

Your task is to determine how many swap operations Ultra-QuickSort needs to perform in order to sort a given input sequence.

Input

The input contains several test cases. Every test case begins with a line that contains a single integer n < 500,000 -- the length of the input sequence. Each of the the following n lines contains a single integer 0 ≤ a[i] ≤ 999,999,999, the i-th input sequence element. Input is terminated by a sequence of length n = 0. This sequence must not be processed.

Output

For every input sequence, your program prints a single line containing an integer number op, the minimum number of swap operations necessary to sort the given input sequence.

Sample Input

5
9
1
0
5
4
3
1
2
3
0

Sample Output

6
0

 

本题其实只要是明白了归并排序,至于分治思想和逆排序就很简单了

归并法排序

思想概述:其实归并排序就是分治思想的一种应用,就是每次用二分的方式,每次将序列分成两部分,然后知道分成每一部分两个数为止,
在这一整块作为一部分,依次从该该整区间的左区间第一个数开始和右边(即从mid右面的)第一个数作比较,将小的放到合并序列中
这样就将该小的范围排好序了。之后一次排序,知道当前区间为整个大区间为止(详解http://blog.csdn.net/u014665013/article/details/38035359

分治思想  http://www.doc88.com/p-98237060283.html

题目本质就是求逆序对了,简单介绍一下。逆序对是指在序列{a0,a1,a2...an}中,若ai<aj(i>j),则(ai,aj)上一对逆序对。而逆序数顾名思义就是序列中逆序对的个数。例如: 1 2 3是顺序,则逆序数是0;1 3 2中(2,3)满足逆序对的条件,所以逆序数只有1; 3 2 1中(1,2)(1,3)(2,3)满足逆序对,所以逆序是3。由定义不能想象,序列n的逆序数范围在[0,n*(n-1)/2],其中顺序时逆序数为 0,完全逆序时逆序数是n*(n-1)/2。

可以利用归并排序时计算逆序个数,时间复杂度是nlog2n,而空间复杂度2n。 利用归并求逆序是指在对子序列s1和s2在归并时,若s1[i]>s2[j](逆序状况),则逆序数加上s1.length-i,因为s1中i后面的数字对于s2[j]都是逆序的 

 

#include <cstdio>
#include <iostream>
using namespace std;
#define MAX 500001

int n, a[MAX], t[MAX];    //a[]为输入序列 t[]为合并序列 应该注意的是,每次t[]数列都是临时的,
                            //只在每次当前区间的排序中记录有序序列
__int64 sum;        //逆序对个数

// 归并  
void Merge(int l, int m, int r)
{
	//p指向输出区间
    int p = 0;
	//i、j指向2个输入区间
    int i=l, j=m+1;                 //2个输入区间都不为空时
	
    while(i<=m && j<=r)
    {                            // 取关键字小的记录转移至输出区间	
        if(a[i]>a[j])         //在左边的区间的当前访问的数大于右区间的
        {
            t[p]=a[j];        //将右区间数合并到合并数列中
            p++;                  
            j++;               //如果合并了,就将右区间的加1
			<span style="color:#ff0000;">sum+=m-i+1; </span>      //a[i]后面的数字对于a[j]都是逆序的 例如1(L) 2 7 9 10(mid) 3 4 6 7 8 (r)                                                                                                                                                        ^(当前位置)这样在7后面的(当然                                                                            小于    =10)的所有数就都大于后面的了所以这样加
        }
        else              //在左边的区间的当前访问的数小于或等于右区间的
		{	
            t[p] = a[i];    //将左区间数合并到合并数列中
			p++;              
			i++;
		}
    }
 
    while(i <= m)         //如果左区间中还存在没有进入合并序列的数,就让剩下的进合并序列
		t[p++] = a[i++];
    while(j <= r)       //如果左区间中还存在没有进入合并序列的数,就让剩下的进合并序列
		t[p++] = a[j++];

    for (i=0; i<p; i++)	 // 归并完成后将结果复制到原输入数组
        a[l+i] = t[i];
}

//   归并排序 
void MergeSort(int l, int r)
{
    int m;
    if (l<r)
    {                           // 将长度为n的输入序列分成两个长度为n/2的子序列 	
        m = (l+r)/2;             	
        MergeSort(l, m);// 对两个子序列分别进行归并排序
        MergeSort(m + 1, r);     	 
       <span style="color:#cc0000;"> Merge(l, m, r);//子序列合并成最终有序序列    //其实完全可以将Merge()函数放在该位置
</span>    }
}

int main()
{
    int i;
    while(1)
    {
        scanf("%d", &n);
        if (n == 0) 
			break;
        sum=0;
        for(i = 0; i < n; i++)
            scanf("%d", &a[i]);
        MergeSort(0, n-1);
        printf("%I64d\n", sum);
    }
    //system("pause");
    return 0;
}