逆序數的幾種求法

轉載原地址:https://blog.csdn.net/Du_Mingm/article/details/81203260

首先,逆序數的定義

什麼叫逆序數

對於某一個數來說,它的逆序數等於在它之前有多少個比它大的數

對於某一個序列來說,逆序數等於所有數的逆序數之和

例如

 序列      5   1    5   2

逆序數   0   1    0   2

序列的逆序數 1+2=3

 

來看逆序數的求法

方法一

首先將定義一個結構體,存數列的值和下標,然後按數值從大到小(數值相同按下標從大到小)sort一下

 

然後建立樹狀數組,從最大的元素開始,將其標記,即   add(p【i】.id,1)

利用其query(i)查詢當前1----i的和

對於第i大的數,由於之前所有比它大的數已經標記,所以query(i)就是當前數的逆序數

 

例如  5  6  3  8  2     其排序之後即是      2   3   5   6   8   將求對應的值的逆序數的問題轉化成了求下標對應的逆序數按求逆

         1  2  3  4  5                                    5   3   1   2    4

序數的方法,先求下標的逆序數  即:   0 + 1 + 2 + 2 + 1  =   6 ,答案即是序列的逆序數,

換一種思路來看,  爲了避免加入順序而導致逆序數的不正確,所以從後面開始逆序,因爲已經轉化成下標的逆序

所以只需看下標即可,  5   3   1   2   4  對於4的逆序只有5,在c[5]+1,

                                                              對於2的逆序有5,3,在c[5]+1,c[3]+1

                                                              其餘類似。。。。。

然後對於3來說的樹狀數組求1~3的和,即是求以3爲逆序數(此例是1,2)的數的個數,

同理對於 i 來求1~i 之間的和,就是來求以 i 爲逆序數的數的個數,最後求個總和,也就是所要求的該數列的逆序數。

代碼:

#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
int c[100010]; 
typedef struct nodee{
	int x,num;
}node;
node maze[10010];
bool cmp(node u,node v)
{
	if(u.x==v.x)
		return u.num>v.num;
	return u.x<v.x;
}
int lowbit(int x)
{
	return x&(-x);
}
void add(int x)
{
	while(x<100005){
		c[x]+=1;
		x+=lowbit(x);
	}
}
int summ(int x)
{
	int sum=0;
	while(x>0){
		sum+=c[x];
		x-=lowbit(x);
	}
	return sum;
}
int main()
{
	int n,i;
	while(scanf("%d",&n)!=EOF){
		
		memset(c,0,sizeof(c));
		
		for(i=1;i<=n;i++){
			scanf("%d",&maze[i].x);
			maze[i].num=i;
		}
		
		sort(maze+1,maze+n+1,cmp);	
		
		int sum=0;
		for(i=n;i>=1;i--){
			int y=maze[i].num;
			sum+=summ(y);
			add(y);
		}
		
		printf("%d\n",sum);	
	}
	
	
	return 0;
 } 

 

方法二

 

也是樹狀數組,不過要離散化處理

所謂的離散化就是將數組排個序,例如從小到大排序,

那麼將第一小記做1,第二小記做2,那麼就算是離散化了

(離散,字面意思,分離,散開,分成一個個的小塊,當然每個塊不能相同)

這個處理的話就是這樣

         序列      5   1    5   2

離散化數組     3   1    3   2

這樣從最小的數開始建立樹狀數組(標記)

那麼i-query(b【i】)就是當前數的逆序數(i爲當前第i個的數,b【i】是當前數是第幾小,

query詢問的是1------i有多少個標記的數(小於等於當前數的數),i-query(b【i】)自然是大於當前數  的數  的個數)

例如實例:maze[i].x:            2   5   7   3   4    排序後爲    maze[i].x           2  3   4   5   7         

                  maze[i].num       1   2   3   4   5                       maze[i].num      1  4   5   2   3    

 

離散化後爲:   maze[i].x                 2    3    4     5    7       按照num的順序走就是:

                        maze[i].num            1    4     5     2    3                             maze[i].num   1    2    3   4   5 

                        b[maze[i].num]        1    2     3     4    5                        b[maze[i].num]    1    4    5   2   3

對於這個例子,maze[i].num 從1~n開始遍歷,先add(1),改變後綴和,將比1大的c[i]都加1,

                                                                 然後add(4),將比4 大的位置都加1

                                                               。。。。。以此類推

然後每一次query(b[i]),就代表在i之前有多少比 b[i] 小的數的個數,因爲 i 代表b[i] 的位置,即前面有幾個數

所以 i-query(b[i]) ,就代表 b[i]  之前有幾個比它大的數的個數。

補充一個

離散化三部曲:

1. 數組 ha[] 存儲所有存在過的數據,sort排序

2. 對ha數組進行去重,重複的數據只保留一個。unique去重(unique函數前提有序)

3. 查詢某個數字離散化之後對應的數字,lower_bound查排名

代碼

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define ll long long
using namespace std;
int n,c[100010];
int lowbit(int x)
{
	return x&(-x);
}
void add(int k,int num)
{
    while(k<=n)
    {
        c[k]+=num;
        k+=lowbit(k);
    }
}
 
int query(int k)
{
    int sum=0;
    while(k)
    {
        sum+=c[k];
        k-=lowbit(k);
    }
    return sum;
}
typedef struct nodee
{
    int x,i;
}node;
node maze[100010];
bool cmp(node u,node v)
{
	if(u.x==v.x)
		return u.i>v.i;
	return u.x<v.x; 
}
int b[100010];

int main(void)
{
    int i,j,x,y;
 
    while(~scanf("%d",&n))
    {
        ll sum = 0;
        memset(c,0,sizeof(c));
        for(i=1;i<=n;i++)
        {
            scanf("%d",&maze[i].x);
            maze[i].i = i;
        }
        sort(maze+1,maze+1+n,cmp);
        
        int cnt = 1;
        for(i=1;i<=n;i++){
            if(i!=1&&maze[i].x!=maze[i-1].x)
                cnt++;
            b[maze[i].i] = cnt;
        }
        
        for(i=1;i<=n;i++)
        {
            add(b[i],1);
            sum += (i-query(b[i]));
        }
        printf("%lld\n",sum);
    }
    return 0;
}

 

 

方法三

 

歸併求解(我 還 不 會)

相當漂亮的寫法,讓我寫肯定寫不了這麼好,但是有個問題是b數組的申請,這裏頻繁申請爆掉了

我給改成了全局變量

#include <bits/stdc++.h>
 
using namespace std;
 
long long int cnt;
int a[104000];
int *b;
void Merge(int a[], int low, int mid, int high);
void Merge_sort(int a[], int low, int high);///歸併排序
 
int main(){
    int n, i;
    scanf("%d", &n);
    for(i = 0; i < n; i++)
        scanf("%d", &a[i]);
    b = new int[n];
    cnt = 0;
    Merge_sort(a, 0, n-1);
    delete[] b;
    printf("%lld\n", cnt);
    return 0;
}
void Merge_sort(int a[], int low, int high){
    int mid;
    if(low < high){
        mid = (low + high)/2;
        Merge_sort(a, low, mid);
        Merge_sort(a, mid+1, high);
        Merge(a, low, mid, high);
    }
}
void Merge(int a[], int low, int mid, int high){///可類比兩組有序鏈表的歸併,思想基本一樣
    int i = low;
    int j = mid + 1;
    int k = 0;
    //int *b = new int[high-low+1];///動態申請內存
    while(i <= mid && j <= high){
        if(a[i] <= a[j])
            b[k++] = a[i++];
        else {
            b[k++] = a[j++];
            cnt += (mid - i + 1);///歸併兩組有序數據,當a[i] > a[j], 則在區間[i, mid]的數據全部大於a[j],此時對於a[j]的逆序數爲(mid - i + 1)
        }
    }
    while(i <= mid){
        b[k++] = a[i++];
    }
    while(j <= high){
        b[k++] = a[j++];
    }
    for(k = 0, i = low; i <= high; i++, k++){
        a[i] = b[k];
    }
}

方法四

直接數 的就不寫了

發佈了59 篇原創文章 · 獲贊 31 · 訪問量 4萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章