一.题目
题目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;
}
#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)的时间来推断元素在排序后的数组的下标与原来数组的下标的关系,略微有些复杂。
这道题使用二分归并排序在进行排序的同一时候,也递归计算量数组中的逆序对的个数。是一种非常有用的方法。
这样的方法主要是基于数组中的逆序对的个数等于子问题中存在的逆序对的个数加上归并过程中存在的逆序对的个数。归并过程中存在的逆序对的个数等于后面一半的元素移动到正确位置时须要移动的位数的和。
版权全部。欢迎转载,转载请注明出处,谢谢