poj 2299 Ultra-QuickSort
歸並排序求逆序對
題目大意
給你多個序列,讓你求出每個序列中逆序對的數量。
輸入:每組數據以一個數 n 開頭,以下n行,每行一個數字,代表這個序列;
輸出:對於輸出對應該組數據的逆序對的數量;
順便在此吐槽一下翻譯器,翻譯了一頓我啥都看不懂(都懷疑自己是不是中國人了),幸虧自己還能看懂點英語啊。
這個題是機房裏一位小夥伴問我我才做的,蒟蒻的我剛開始居然想要雙重循環(類似於冒泡排序的方法)來做,看完數據範圍之後就放棄了;
然後想到了逆序對是使用歸並排序來做的,所以就自己手碼了一個歸並排序;;可能有的小夥伴還不知道歸並排序的思想,所以看了一晚上歸並排序的蒟蒻——我,就來給小夥伴們介紹一下吧!
歸並排序:
nlog(n)的穩定算法(可用於求逆序對的個數)
應用方法:
二分(所以又叫二路歸並)+遞歸;
為什麽使用遞歸?
answer:要使用歸並排序首先就要將數據分解,一直分解到每一個單位,然後就是進行合並了;
如何合並?
answer:比較a[i]和a[j]的大小(其中a[i]屬於左區間,a[j]屬於右區間,其實就是將左右區間合並、並排序),若a[i]<a[j],則將a[i]復制到r[k]中,然後將r和k都加1,否則將a[j]復制到r[k]中,將r,k加1,最後再將r[k]移動到a[i]中,然後繼續合並;
如何求逆序對?
answer:
下面就是 歸並排序求逆序對 的過程==
1 #include<cstdio> 2 using namespace std; 3 const int maxn=5e5+5; 4 int a[maxn],r[maxn],n;//r[]是輔助用的; 5 long long ans;//ans作為全局變量記錄每次逆序對的數量; 6 //記得ans要開long long,否則WAWAWA 7 void msort(int s,int t){ 8 if(s==t) return; 9 int mid=(s+t)>>1;//二進制下右移一位,相當於 /2 ,但是速度更快! 10 msort(s,mid),msort(mid+1poj2299,t);//遞歸的體現 11 int i=s,j=mid+1,k=s; 12 while(i<=mid&&j<=t) 13 if(a[i]<=a[j]) r[k]=a[i],i++,k++; 14 else r[k]=a[j],j++,k++,ans+=mid-i+1; 15 //ans的計算是最神奇的地方,不過動動腦子,畫個圖啥的也是可以想出來的 16 while(i<=mid) r[k]=a[i],i++,k++; 17 while(j<=t) r[k]=a[j],j++,k++; 18 for(int i=s;i<=t;i++) a[i]=r[i];//每次要更新的是 a[]數組!! 19 } 20 int main(){ 21 while(1){//來一個無限的循環== 22 scanf("%d",&n); 23 if(!n) return 0;//(!n相當於n==0,當然速度也是快一點的啦!)n=0就直接結束程序; 24 for(int i=1;i<=n;i++) scanf("%d",&a[i]); 25 msort(1,n); 26 printf("%lld\n",ans); 27 ans=0;//註意:ans每次都需要清0; 28 } 29 }
--------------------------------------------------下方高能--------------------------------------------
其實只是一個實例,解釋一下ans是如何求出來的啦
a[i]↓ ←mid=4→ a[j]↓
3 4 7 9 1 5 8 10
首先將右區間的 1 取出,放到r[k]中,此時 1 是比每個a[i]中的元素都小,也就是說此時i的指針指向 a[1] 的位置,此刻得到的逆序對的數量為 4 ; r[k]= 1 ;
然後再將a[i]和a[j]比較(直到a[i]<a[j]),a[i]<a[j] 將a[i]的元素放到r[k]中; r[k]= 1 3 4;現在a[j]>a[i], i 指向 a[3] 的位置,將5 放到 r[k] 中,得到的逆序對數量為 2 ; r[k]= 1 3 4 5
以此類推,直到進行完歸並排序,每次合並都會求出逆序對的數目,即 mid-i+1 ,最後每次將 ans 加上 mid-i+1即可得到最後的答案;
其實求逆序對呢,還可以用線段樹,不過對於如此蒟的蒟蒻我,還是算了吧,有興趣的小夥伴也可以自己從網上搜著看一下,蒟蒻在此就不介紹給大家了(我也不會啊)!
poj 2299 Ultra-QuickSort