计算逆序数对&&重要逆序数对(significant inversion)

文章目录

逆序数对简介

  • 数组中的两个元素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;
}
    原文作者:haiki66
    原文地址: https://blog.csdn.net/haiki66/article/details/105115269
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞