標籤:
題目連結:http://acm.hust.edu.cn/vjudge/contest/view.action?cid=85904#problem/A
題意:
多組案例,先輸入資料的數目,在依次輸入資料,判斷將這些資料從小到大排列最少需要多少操作步數。(案例以輸入0結束)
案例:
Sample Input
5
9
1
0
5
4
3
1
2
3
0
Sample Output
6
0
分析:
這一題我參考了一位大神的思路分析,覺得講解挺詳細的,也給了我做題思路,所以借鑒了過來。具體如下:
題目本質就是求逆序對了,簡單介紹一下。逆序對是指在序列 {a0,a1,a2...an}中,若ai<aj(i>j),則(ai,aj)上一對逆序對。而逆序數顧名思義就是序列中逆序對的個數。例如: 1 2 3是順序,則逆序數是0;1 3 2中(2,3)滿足逆序對的條件,所以逆序數只有1; 3 2 1中(1,2)(1,3)(2,3)滿足逆序對,所以逆序是3。由定義不能想象,序列n的逆序數範圍在[0,n*(n-1)/2],其中順序時逆序數為 0,完全逆序時逆序數是n*(n-1)/2。
可以利用歸併排序時計算逆序個數,時間複雜度是nlog2n,而空間複雜度 2n。 利用歸併求逆序是指在對子序列s1和s2在歸併時,若s1[i]>s2[j](逆序狀況),則逆序數加上s1.length-i,因為s1 中i後面的數字對於s2[j]都是逆序的。具體看注釋吧。
歸併演算法基本思路
設兩個有序的子檔案放在同一向量中相鄰的位置上:R[low..m],R[m+1..high],先將它們合并到一個局部的暫存向量R1中,待合并完成後將R1複製回R[low..high]中。
合并過程中,設定i,j和p三個指標,其初值分別指向這三個記錄區的起始位置。合并時依次比較R[i]和R[j]的關鍵字,取關鍵字較小的記錄複製到R1[p]中,然後將被複製記錄的指標i或j加1,以及指向複製位置的指標p加1。
重複這一過程直至兩個輸入的子檔案有一個已全部複製完畢(不妨稱其為空白),此時將另一非空的子檔案中剩餘記錄依次複製到R1中即可。
原始碼:
1 #include <cstdio> 2 #define MAX 500001 3 int a[MAX], b[MAX],i; 4 long long ans; 5 void merge(int l,int m,int r)//歸併 6 { 7 int p=0,i=l,j=m+1;//p指向輸出,i,j指向輸入 8 while(i<=m&&j<=r)//兩個輸入空間不為0 9 {10 if(a[i]>a[j])//取最小值轉入輸出空間11 {12 b[p++]=a[j++];13 ans+=m-i+1;//a[i]後數值相對於a[i]為逆序14 }15 else16 b[p++]=a[i++];17 }18 while(i<=m) b[p++]=a[i++];//非空輸入空間轉入輸出空間19 for(i=0;i<p;++i)//歸併結束,結果複製到原輸入數組20 a[l+i]=b[i];21 }22 void Mergesort(int l2,int r2)//歸併排序23 {24 if(l2<r2)25 {26 int m2=(l2+r2)/2;//將長為n的序列分為兩部分27 Mergesort(l2,m2);//對子序列排序28 Mergesort(m2+1,r2);29 merge(l2,m2,r2);//合并子序列30 }31 }32 int main( )33 {34 int N;35 while(scanf("%d",&N)&&N)36 {37 ans=0;38 for(i=0;i<N;i++)39 scanf("%d",&a[i]);40 Mergesort(0,N-1);41 printf("%lld\n",ans);42 }43 return 0;44 }
Ultra-QuickSort(數組排序問題)