POJ-2299 Ultra-QuickSort

题目链接: http://poj.org/problem?id=2299
题目大意:
给你N个数组成的序列,求这个序列的逆序数一共有多少个。这个序列个数不超过100000.

解题思路:
很经典的一个问题,解决方法有两种:第一种方法是归并排序,第二种方法是树状数组。
如果我们要枚举的话,复杂度为o(n*n),铁定TLE,所以要考虑更高效的算法。归并操作中,当取到右边集合的第i个元素时,左边集合中元素的个数就是这个元素的逆序数,以此类推,把右边集合全部的逆序数求出来,求和,就是这个序列的逆序数。

代码如下:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<climits>
#include<cstdlib>
using namespace std;
#define N 1000010
int a[N], t[N];
long long sum;

void merge(int low, int mid, int high) //归并操作
{
	int k, begin1, begin2, end1, end2;
	begin1 = low;
	end1 = mid;
	begin2 = mid + 1;
	end2 = high;

	//比较两个指针所指向的元素,选择相对小的元素放入到合并空间,并移动指针到下一位置
	for(k = 0; begin1 <= end1 && begin2 <= end2; ++k)
	{
		if(a[begin1] <= a[begin2]) //小的传送至辅助数组
			t[k] = a[begin1++];
		else
		{
			t[k] = a[begin2++];
			sum += mid - begin1 + 1; //左边剩下的数个数全是逆序数
		}
	}

	if(begin1 <= end1) //若第一个序列有剩余,直接拷贝出来粘到合并序列尾
		memcpy(t + k, a + begin1, (end1 - begin1 + 1) * sizeof(int));
	else //若第二个序列有剩余,直接拷贝出来粘到合并序列尾
		memcpy(t + k, a + begin2, (end2 - begin2 + 1) * sizeof(int));
	//将排序好的序列拷贝回数组中
	memcpy(a + low, t, (high - low + 1) * sizeof(int));
}

void merge_sort(int begin, int end)
{
	int mid;
	if(begin < end)
	{
		mid = (end + begin) / 2;
		merge_sort(begin, mid);
		merge_sort(mid + 1, end);
		merge(begin, mid, end);
	}
}

int main()
{
	int ncase;
	int num;
	while(scanf("%d", &num) && num)
	{
		sum = 0;
		for(int i = 0; i < num; ++i)
			scanf("%d", &a[i]);
		merge_sort(0, num - 1);
		printf("%lld\n", sum);
	}
	return 0;
}


你可能感兴趣的:(算法,merge)