一.题目
题目1 : 二分·归并排序之逆序对
时间限制:10000ms
单点时限:1000ms
内存限制:256MB
-
描述
在上一回、上上回以及上上上回里我们知道Nettle在玩《艦これ》。经过了一番苦战之后,Nettle又获得了的很多很多的船。
这一天Nettle在检查自己的舰队列表:
我们可以看到,船默认排序是以等级为参数。但实际上一个船的火力值和等级的关系并不大,所以会存在A船比B船等级高,但是A船火力却低于B船这样的情况。比如上图中77级的飞龙改二火力就小于55级的夕立改二。
现在Nettle将按照等级高低的顺序给出所有船的火力值,请你计算出一共有多少对船满足上面提到的这种情况。
输入
第1行:1个整数N。N表示舰船数量, 1≤N≤100,000
第2行:N个整数,第i个数表示等级第i低的船的火力值a[i],1≤a[i]≤2^31-1。
输出
第1行:一个整数,表示有多少对船满足“A船比B船等级高,但是A船火力低于B船”。
- 样例输入
-
10 1559614248 709366232 500801802 128741032 1669935692 1993231896 369000208 381379206 962247088 237855491
- 样例输出
-
27
二.解题技巧
根据题目的提示,这道题可以使用二分归并排序来对数组进行排序的同时,统计逆序数的个数。具体做法是将两个子问题进行二分归并排序后得到的逆序数加上将子问题进行归并的时候,后面一半的元素移动的位置来得到最终的逆序数。
三.实现代码
#include <iostream>
#include <vector>
using namespace std;
long long Merge(vector<int>::iterator BeginIte, int Number)
{
if (Number < 2)
{
return 0;
}
int Mid = Number / 2;
int IndexFirst = 0;
int IndexSecond = Mid;
vector<int> TmpArray;
long long Count = 0;
int Index = 0;
while ((IndexFirst < Mid) && (IndexSecond < Number))
{
int TmpFirst = *(BeginIte + IndexFirst);
int TmpSecond = *(BeginIte + IndexSecond);
if (TmpFirst > TmpSecond)
{
TmpArray.push_back(TmpSecond);
Count += IndexSecond++ - Index++;
}
else
{
TmpArray.push_back(TmpFirst);
IndexFirst++;
Index++;
}
}
while (IndexFirst < Mid)
{
TmpArray.push_back(*(BeginIte + IndexFirst++));
}
while (IndexSecond < Number)
{
TmpArray.push_back(*(BeginIte + IndexSecond++));
}
// copy the array back
for (int Index = 0; Index < Number; Index++)
{
*(BeginIte++) = TmpArray[Index];
}
return Count;
}
long long MergeSort(vector<int>::iterator BeginIte, int Number)
{
if (Number < 2)
{
return 0;
}
int Mid = Number / 2;
long long Count = 0;
Count += MergeSort(BeginIte, Mid);
Count += MergeSort(BeginIte + Mid, Number - Mid);
Count += Merge(BeginIte, Number);
return Count;
}
int main()
{
int Number = 0;
cin >> Number;
int Index = 0;
vector<int> Array;
while (Index++ < Number)
{
int Tmp = 0;
cin >> Tmp;
Array.push_back(Tmp);
}
cout << MergeSort(Array.begin(), Number) << endl;
// for (int Index = 0; Index < Number; Index++)
// {
// cout << Array[Index] << endl;
// }
// cout << "The result is " << Count << endl;
return 0;
}
四.体会
这道题让我长了见识,我记得我以前的做法是先使用STL里面的sort函数对数组进行排序,然后根据排序后的数组的元素的下标与相同元素在原来数组里面的下标的关系,来计算逆序对的个数(也就是逆序数)。这种方法不仅需要进行排序的时间O(nlgn),在使用hash_table的时候,还需要O(n)的时间来判断元素在排序后的数组的下标与原来数组的下标的关系,稍微有些复杂。
这道题使用二分归并排序在进行排序的同时,也递归计算量数组中的逆序对的个数,是一种很实用的方法。这种方法主要是基于数组中的逆序对的个数等于子问题中存在的逆序对的个数加上归并过程中存在的逆序对的个数。归并过程中存在的逆序对的个数等于后面一半的元素移动到正确位置时需要移动的位数的和。
版权所有,欢迎转载,转载请注明出处,谢谢