文章目录
逆序数对简介
- 数组中的两个元素A[i], A[j],如果下标 i < j,但 A[i] > A[j] ,称 A[i] 与 A[j] 为逆序数对
计算逆序数对思路
- Divide and Conquer
- 将数组根据下标不断划分为两部分,然后从底至上计算每两部分间的逆序数对个数
- 计算时,如果这两部分是 unstructured,那么就需要两层for循环,一一对比,O(n^2)
- 引入一些结构,比如两部分都按照升序排好序,那么归并排序的combine时就可以直接得到计数,O(nlogn)
- 完全利用归并排序,在 merge 时加入一句话即可:参见这篇文章
significant inversion 简介
- 数组中的两个元素A[i], A[j],如果下标 i < j,但 A[i] > n*A[j] ,称 A[i] 与 A[j] 为逆序数对
significant inversion 思路
- 思路与“普通逆序对”(i<j⋀ai>aj)的思路完全一致,也是在归并排序的归并过程中计算逆序对个数
- 重要逆序对的判定条件(“前面的数是后面的两倍”)比归并条件(“前面的数比后面的数大”)更严格,因此要多加一层判断
- 第一种是 在归并前另写一个循环单独计算重要逆序对,第二种是一 边排序一边计算
significant inversion 代码
#include<iostream>
#include<vector>
using namespace std;
int cnt = 0;
void SignificantInversion(vector<int> v,int l, int mid, int r){
int i = l, j = mid+1;
vector<int> tmp(v);
while(i <= mid && j <= r){
if(tmp[i] > 2*tmp[j]){
cnt += mid - i + 1;//左右两边是排好序的数组,如果当前左边的元素是右边元素
//的二倍,那么左边数组后面的元素都比当前元素大二倍,这段长度就是中间减i
//说实话,我看网上的公式都特别复杂,有没有人看一下这个对吗?
j++;
}else{
i++;
}
}
i = l, j = mid+1;
int k = l;
while(i <= mid && j <= r){
if(tmp[i] > tmp[j]){
v[k++] = tmp[j++];
}else
v[k++] = tmp[i++];
}
while(i <= mid){
v[k++] = tmp[i++];
}
while(j <= r)
v[k++] = tmp[j++];
}
void CountInversion(vector<int> v, int l, int r){
if(l < r){
int mid = l + (r-l)/2;
CountInversion(v,l,mid);
CountInversion(v,mid+1,r);
SignificantInversion(v,l,mid,r);
}
}
int main(){
vector<int> v{ 10,2};
CountInversion(v,0,v.size()-1);//第二个参数是 n-1!!!
cout<<cnt<<endl;
}
吐槽一下,一道题做了n多个小时,本来可以自己想,硬是各种百度,看到各种晦涩难懂的答案,简直醉了。最主要的原因就是,自己只看了别人的普通逆序对是怎么做的,自己没去想,哭… 其实现在这个解法我也只是用几个案例跑了一下,也不知道对不对(卒)
参考了这篇,还讲得比较清楚
听别人说不能在排序的时候比较,但我这样做感觉也是对的呀!!
#include<iostream>
#include<vector>
using namespace std;
int cnt = 0;
void SignificantInversion(vector<int> v,int l, int mid, int r){
int i = l, j = mid+1;
int k = l;
vector<int> tmp(v);
while(i <= mid && j <= r){
if(tmp[i] > tmp[j]){
if(tmp[i] > 2*tmp[j]){
cnt+=mid-i+1;
//cout<<"i:"<<i<<"cnt:"<<cnt<<endl;
}
v[k++] = tmp[j++];
}else{
v[k++] = tmp[i++];
}
}
while(i <= mid){
v[k++] = tmp[i++];
}
while(j <= r){
v[k++] = tmp[j++];
}
}
void CountInversion(vector<int> v, int l, int r){
if(l < r){
int mid = l + (r-l)/2;
CountInversion(v,l,mid);
CountInversion(v,mid+1,r);
SignificantInversion(v,l,mid,r);
}
}
int main(){
vector<int> v{ 5,9,3,5,3,1};
CountInversion(v,0,v.size()-1);
cout<<cnt<<endl;
}