求逆序對的三種方法


逆序對

在數組A[x]中,若存在(i < j) && (A[i] > A[j]),則稱(A[i],A[j])為數組A[x]的一個逆序對


暴力O(N^2)求法

    for(i = 1; i <= n; i ++)
        for(j = i; j <= n; j ++)
            if(A[i] > A[j])cnt ++;

歸並排序求法

由上面的表述可知,逆序對同樣可以表示為:在數組A[x]中,若存在(i > j) && (A[i] < A[j]),則稱(A[i],A[j])為數組A[x]的一個逆序對
那么,設cnt[i]為A[i]前比它大的數字個數,逆序對數則 = cnt[1] + cnt[2] + cnt[3] + ... + cnt[n];
一只總逆序對數為每個數字前比它大的數字個數之和,那么我們只需要在歸並排序中統計這個數字個數即可

#include<iostream>
...
void merge(int L, int R, int Mid){
    int i = L;int j = Mid + 1;int k = L;
    while(i <= Mid && j <= R){
        if(s1[i] <= s1[j])s2[k ++] = s1[i ++];
        else{
            cnt += Mid - i + 1;
            //對於i到Mid這Mid - i + 1個數字(假設它為j),每一個a[j]都與a[i]構成了一個逆序對
            s2[k ++] = s1[j ++]; 
        }
    }
    while(i <= Mid)s2[k ++] = s1[i ++];
    //對於每個j都已討論完,每個j錢比他大的數都已計入cnt,所以此時不用更新cnt
    while(j <= R)s2[k ++] = s1[j ++];
    for(i = L; i <= R; i ++)s1[i] = s2[i]; 
}
void mergesort(int L, int R){
    if(L < R){
        int Mid = (L + R) / 2;
        mergesort(L, Mid),mergesort(Mid + 1, R);
        merge(L, R, Mid);
    }
}
int main(){
	scanf("%d", &n);
	for(int i = 1; i <= n; i ++)scanf("%d", &s1[i]);
	mergesort(1, n);
	for(int i = 1; i <= n; i ++)printf("%d", s1[i]);
}

樹狀數組求逆序對

虛擬一個數組A[i],A[i] == 1表示出現了i,A[i] == 0表示沒有出現i
利用樹狀數組,getsum(i)表示i~i中出現的數字個數
邊輸入邊進行操作,對於當前的數字x,它所貢獻的逆序對書即為在它之前出現的比它大的數字的總數,即為當前x~n的sum值->getsum(n)-getsum(x);之后再將A[i] ++,更新C[i](modify)

#include<iostream>
#define lowbit(x) x & -x
...
void modify(int x, int d){
	for(int i = x; i <= n; i += lowbit(i))c[i] += d;
}
int getsum(int x){
	int sum = 0;
	for(int i = x; i >= 1; i -= lowbit(i))sum += c[i];
	return sum;
}
int main(){
	scanf("%d", &n);
	for(int i = 1; i <= n; i ++){
		scanf("%d", &x);
		ans += getsum(n) - getsum(x);
		modify(x, 1)''
	}
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM