1. 程式人生 > >51Nod1019 逆序數(歸併排序詳解)

51Nod1019 逆序數(歸併排序詳解)

逆序對

給定一個1-N的排列A1, A2, ... AN,如果Ai和Aj滿足i < j且Ai > Aj,我們就稱(Ai, Aj)是一個逆序對。  

求A1, A2 ... AN中所有逆序對的數目。

input

第一行包含一個整數N。  

第二行包含N個兩兩不同整數A1, A2, ... AN。(1 <= Ai <= N)  

對於60%的資料 1 <= N <= 1000  

對於100%的資料 1 <= N <= 100000

output

一個整數代表答案

Sample Input

5
3 2 4 5 1

Sample Output

5

Time limit

10000 ms

Memory limit

262144 kB

本題由於n可能等於100000,如果用普通思想的遍歷整個資料,時間複雜度最壞為0(n^2),時間必然超時。所以本題要用歸併排序演算法。

下面簡單介紹一下歸併演算法的邏輯:

例如一個數組5 ,3, 4, 6 ,9, 1。

其中用到了分治演算法,分成兩邊同時進行,增加演算法效率,歸併演算法必然是分了lgn次。演算法的時間複雜度最壞最好平均都是lgn。

具體思路是:先找到一個mid,分成兩邊同時拆分成有序陣列(一個數,必然是有序的,所以就拆成一個數):先拆成2個數組分別是:5 ,3 ,4 。    6  ,9  ,1。

然後拆成4個數組 5,3。   4。   6,9。    1。

繼續拆成6個數組5 3 4 6 9 1

拆完之後就是合併,定義一個空的陣列用於盛裝:5和3比較,5大,先將3賦給新定義的陣列,然後是5。3,5.和4比較,3大,將3賦給陣列,然後是4和5比較,4大,將4賦給陣列,最後將5賦給陣列。同時6 9 1進行相同的操作。最後是3 4 5 和1 6 9比較得到1 3 4 5 6 9.將這個陣列複製給原來的陣列,就完成了排序。

這樣先用遞拆分陣列,在合數列就完成了歸併排序。

歸併排序的程式碼:

拆分:

void mergesort(int *a,int first,int last,int *temp)
{
	if(first<last)
	{
		int mid=(first+last)/2;
		mergesort(a,first,mid,temp);//左邊有序 
		mergesort(a,mid+1,last,temp);//右邊有序 
		MergeArray(a,first,mid,last,temp);//在將二個有序數列合併 
	}
}

合併:

void MergeArray(int *a,int first,int mid,int last,int *temp)
{//將有二個有序數列a[first...mid]和a[mid...last]合併。
	int k=0;
	int i=first,j=mid+1;
	int n=mid,m=last;
	while(i<=n&&j<=m)
	{
		if(a[i]<a[j])
			temp[k++]=a[i++];
		else
			temp[k++]=a[j++];
	}
	while(i<=n)
		temp[k++]=a[i++];
	while(j<=m)
		temp[k++]=a[j++];
	for(i=0;i<k;i++)
		a[first+i]=temp[i];
}

定義新的陣列:

bool MergeSort(int *a,int n)
{
	int *temp;
	temp=(int *)malloc(sizeof(int)*n);
	mergesort(a,0,n-1,temp);
	free(temp);
}

實現一個n長度的陣列排序:

#include<stdio.h>
#include<stdlib.h>
void MergeArray(int *a,int first,int mid,int last,int *temp)
{//將有二個有序數列a[first...mid]和a[mid...last]合併。
	int k=0;
	int i=first,j=mid+1;
	int n=mid,m=last;
	while(i<=n&&j<=m)
	{
		if(a[i]<a[j])
			temp[k++]=a[i++];
		else
			temp[k++]=a[j++];
	}
	while(i<=n)
		temp[k++]=a[i++];
	while(j<=m)
		temp[k++]=a[j++];
	for(i=0;i<k;i++)
		a[first+i]=temp[i];
}
void mergesort(int *a,int first,int last,int *temp)
{
	if(first<last)
	{
		int mid=(first+last)/2;
		mergesort(a,first,mid,temp);//左邊有序 
		mergesort(a,mid+1,last,temp);//右邊有序 
		MergeArray(a,first,mid,last,temp);//在將二個有序數列合併 
	}
}
bool MergeSort(int *a,int n)
{
	int *temp;
	temp=(int *)malloc(sizeof(int)*n);
	mergesort(a,0,n-1,temp);
	free(temp);
}
int main()
{
	int i,n;
	scanf("%d",&n);
	int *a;
	a=(int *)malloc(sizeof(int)*n);
	for(i=0;i<n;i++)
		scanf("%d",&a[i]);
	MergeSort(a,n);
	for(i=0;i<n;i++)
		printf("%d ",a[i]);
	free(a);
}

談回逆序對這道題。

通過對歸併演算法的合併比較兩個數大小的操作,可以記錄逆序對。

具體操作為(合併環節裡):

void MergeArray(long long int *a,int first,int mid,int last,int *temp)
{
	int k=0;
	int i=first,j=mid+1;
	int n=mid,m=last;
	while(i<=n&&j<=m)
	{
		if(a[i]<=a[j])
		{			
			temp[k++]=a[i++];
		}		
		else
		{
			l+=n-i+1;
			temp[k++]=a[j++];
		}
								
	}
	while(i<=n)
		temp[k++]=a[i++];	
	while(j<=m)
		temp[k++]=a[j++];
	for(i=0;i<k;i++)
		a[first+i]=temp[i];
}

AC程式碼:

#include<stdio.h>
#include<stdlib.h>
long long int l=0;
void MergeArray(long long int *a,int first,int mid,int last,int *temp)
{
	int k=0;
	int i=first,j=mid+1;
	int n=mid,m=last;
	while(i<=n&&j<=m)
	{
		if(a[i]<=a[j])
		{			
			temp[k++]=a[i++];
		}		
		else
		{
			l+=n-i+1;
			temp[k++]=a[j++];
		}
								
	}
	while(i<=n)
		temp[k++]=a[i++];	
	while(j<=m)
		temp[k++]=a[j++];
	for(i=0;i<k;i++)
		a[first+i]=temp[i];
}
void mergesort(long long int *a,int first,int last,int *temp)
{
	if(first<last)
	{
		int mid=(first+last)/2;	
		mergesort(a,mid+1,last,temp);
		mergesort(a,first,mid,temp);
		MergeArray(a,first,mid,last,temp);
	}
}
bool MergeSort(long long int *a,int n)
{
	int *temp;
	temp=(int *)malloc(sizeof(int)*n);
	mergesort(a,0,n-1,temp);
	free(temp);
}
int main()
{
	int i,n;
	while(scanf("%d",&n)!=EOF){	
	long long int *a;
	a=(long long int *)malloc(sizeof(long long int)*n);
	for(i=0;i<n;i++)
		scanf("%lld",&a[i]);
	MergeSort(a,n);
	free(a);
	printf("%lld\n",l);
	}
	return 0;
}

注意:本題的資料很大,需要定義一個long long int儲存逆序對,定義一個long long int的陣列儲存需要排序的數。

(一直AC不了,就是因為這個範圍錯了。。。。。)

博主學習的時候,參考的大神部落格地址https://blog.csdn.net/yuehailin/article/details/68961304

相關推薦

51Nod1019 序數歸併排序

逆序對 給定一個1-N的排列A1, A2, ... AN,如果Ai和Aj滿足i < j且Ai > Aj,我們就稱(Ai, Aj)是一個逆序對。   求A1, A2 ... AN中所有逆序對的數目。 input 第一行包含一個整數N。   第二行包含N

51Nod - 1019 序數 歸併排序

在一個排列中,如果一對數的前後位置與大小順序相反,即前面的數大於後面的數,那麼它們就稱為一個逆序。一個排列中逆序的總數就稱為這個排列的逆序數。 如2 4 3 1中,2 1,4 3,4 1,3 1是逆序,逆序數是4。給出一個整數序列,求該序列的逆序數。  

歸併排序附python實現

一、介紹 歸併排序(Merge Sort)指的是利用分治和遞迴的思想,對一個亂序的數列進行排序。 所謂“分”,指的是將一個亂序數列不斷進行二分,得到許多短的序列。 所謂“治”,指的是將這些短序列進行兩兩合併,然後將合併的結果作為新的序列,再與其他序列進行合併,

【ZCMU1203】序數歸併

題目連結 1203: 逆序數 Time Limit: 1 Sec  Memory Limit: 128 MB Submit: 596  Solved: 130 [Submit][Status][Web Board] Description 在一個排列中,如果一對數的前

經典排序歸併排序

歸併排序 一.概述 這裡歸併的含義將兩個或兩個以上的有序表組合成一個新有序表,本文講述二路歸併排序。 二、排序過程 初始序列看成n個有序子序列,每個子序列長度為1 兩兩合併,得到(n/2向下取整數)個長度為2或1的有序子序列 再兩兩合併,重複直至得到一個長度為n的有序序列為止

歸併排序和動畫

歸併排序演算法思想:將陣列不斷二分得到子陣列,知道子陣列長度為1(自然是排序好的),對左子陣列和右子陣列分別排序, 子陣列長度為1,2,4...., 子陣列排序使用了一個輔助陣列 def exchange(arr,i,j): temp=arr[i] ar

希爾排序演算法排序

希爾排序  基本思想 希爾排序(Shell Sort)是插入排序的一種。也稱縮小增量排序,是直接插入排序演算法的一種更高效的改進版本。希爾排序是非穩定排序演算法。該方法因DL.Shell於1959年提出而得名。希爾排序是記錄按下標的一定增量分組,對每組使用直接插入排序演算法

歸併排序,Java版描述。

為了簡單起見,使用int型別陣列講述歸併演算法,後面擴充套件到其他型別的排序。 目錄 1.1 用具體例子說明 十人排序問題。 將十人均分為兩隊 五人分為三人,二人兩隊 對於三人的隊伍,再次分成兩人和一人的隊伍 對於兩

java實現的歸併排序

歸併排序的基本原理為: 一:拆分,假設有N個元素的列表,將這個列表拆分成2個或兩個以上元素組成的新的列表,然後分別對子列表進行排序 二:歸併,把所有的排好序的子類表兩兩歸併,如此重複,直到歸併成一個含N個有序列表為止 歸併排序其實和快速排序都是同一種思想的排序演算法,其採用

歸併排序

歸併排序的想法其實是比較簡單的,但是實現起來卻並沒有那麼容易。現在我們思考如可將兩個區域性有序的兩個陣列組合成一個有序的序列,我們有An,Bn兩個序列且區域性有序,首先我們要新開闢一個新的陣列空間Cn。然後我們從An,Bn兩個序列中依次選取合適的元素加入Cn,我們要用兩個變數來標記An,Bn

排序】快速排序及其非遞迴實現,歸併排序

快速排序   快速排序(Quicksort)是對氣泡排序的一種改進。   我們知道快速排序用的是分治的基本思想:將原問題分解為若干個規模更小但結構與原問題相似的子問題。遞迴的解決這些子問題,然後將這些子問題的解組合為原問題的解。   快速排序的基本思想是:

JAVA學習路線圖一文

-h 基礎 tex 蝸牛 學習路線 jdbc sql https ase 此乃是java攻城獅的學習路線圖,由簡到繁,由易到難,一步步的學習,最後成為JAVA攻城獅。 階段1 1:學習HTML 2:學習CSS 3:JavaScript

json to beanJSONObject類

返回 ddr pre throw expr static urn win ash 原博客地址:http://blog.csdn.net/harrison2010/article/details/43700991 1 方式一 2 /** 3 * Creat

elasticsearch-.yml中文配置

explicit 生產環境 設置 綁定 端口 after over rms make # ======================== Elasticsearch Configuration =========================## NOTE: Elast

如何更加安全、高效地選擇開源項目內附

編譯 com 再次 即時聊天 能力 時代 核心 只需要 重新編譯   前言在平時的開發過程中,難免會遇到這樣那樣的難題,或者一些繁瑣且不想純手工完成的功能,對於這些問題,解決的姿勢有很多種,可以通過同事間的交流、上網查資料、去官網找文檔等,隨著開源的推動和完善,尋找合適的開

字符串哈希算法以ELFHash

不為 查詢 查看 i++ 結果 amp 直接 ble 散列函數 更多字符串哈希算法請參考:http://blog.csdn.net/AlburtHoffman/article/details/19641123 先來了解一下何為哈希: 哈希表是根據設定的哈希函數H(key)和

Python輸出hello world各行命令

類型 路由 app session ssi 文件類型 reload env 定位 創建main.py文件並粘貼下面代碼 點擊右鍵運行Debug ‘main‘後,下方的Debug窗口會出現ImportError: No module named ‘bottle‘這樣的提示,

華為交換機私有hybird接口模式:案例+原理

華為 hybird 華為交換機私有hybird接口模式:(案例+原理詳解) 實驗說明: 準備:如圖pc1 pc2同屬於VLAN10,配置相應的ippc3 pc4同屬於VLAN20 配置相應的ipClient 屬於 VLAN30 配置pc1同網段ipPc1 pc2 client 屬於同網段

Javascript二函數

弱類型語言 9.1 指定 fun 並不會 val 避免 中國 width 一.函數 Javascript是一門基於對象的腳本語言,代碼復用的單位是函數,但它的函數比結構化程序設計語言的函數功能更豐富。JavaScript語言中的函數是“一等公民”,它可

易語云模組-易語言郵件傳送模組下載含命令

易語言支援庫自帶郵件傳送命令,但是和現在的主流郵箱好像不能連線上,收件發件就更別提了。 於是易語云模組就橫空出世了,如果您要開發e程式傳送郵件的話,那麼使用易語云模組就可以很輕鬆的搞定了。 點選此處進入官網下載模組 下面我們進入正題 1.關於易語云模組的命令    我們開啟易語言