本來今天poj崩掉了,並且求逆序對也是個很簡單的問題,羅黑上的分治的題也都刷完了(其實難得一見上羅黑的練習題上的簡單題目),東哥的題又刷不動,打算今天就到這了
但是一想到以前也沒有總結過逆序對的求法,寫完這個總結在做一道每日一題就休息了;
先認識一下什麼是逆序對,舉一個例子:
上述寫的夠清楚了吧(電腦寫字很爛,見諒)
歸併排序我相信不用多介紹了吧?都相信已經學了,算了,要不概括一下歸併排序的核心吧:
歸併排序的核心其實就是先將每個元素初始化為子集,兩兩子集合並,實現內部排序,一直這樣處理知道最後剩下一個集合
稍微說一下合併:
要歸併兩個有序的子序列
例如我們要歸併a[]={13,94,99},{34,56}這兩個子序列
將i,j分別指向子序列的第一個數,進行一次比較發現a[i]<a[j],則將a[i]放在輔助陣列b中,經過四次比較
最終可得b[]={13,34,56,94,99}
第二次比較
第三次比較
第四次比較
具體來看題目:
http://acm.hdu.edu.cn/showproblem.php?pid=4911
題目大意:
給定一個陣列,交換相鄰任意兩個元素,且不超過k次,求最少的逆序對有多少?
題目分析:當k=0的時候即求原始陣列中有多少對逆序對,並且在每次合併中,如果子序列內部是有序的則不存在逆序對;
在合併兩個子序列時,若前子序列的元素大於後子序列的元素則產生逆序對,像上面四次合併,並且在每次合併的時候,可能出現不止一對逆序對,
例如在第二次合併的時候,就出現了{34,96},{34,99}兩對逆序對
那分別討論原始陣列中的逆序對cnt,若cnt<=k則說明不夠交換k次,即最少逆序對為0對
若cnt>k則說明k次交換都發生在逆序的相鄰元素上,則有最少cnt-k對
解題思路:分治法,歸併排序
難度評價:易
參考程式碼
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define int long long 4 #define IOS ios_base::sync_with_stdio(0);cin.tie(0);cout.tie(0); 5 const int N=1e5+10; 6 int a[N]; 7 int b[N]; 8 int cnt; 9 int n,k; 10 void Merge(int L,int mid,int R) 11 { 12 //歸併 13 int i=L; 14 int j=mid+1; 15 int t=0; 16 while(i<=mid&&j<=R) 17 { 18 if(a[i]>a[j]) 19 { 20 b[t++]=a[j++]; 21 cnt+=mid-i+1;//出現逆序對 22 } 23 else 24 b[t++]=a[i++]; 25 } 26 //檢查 27 //一個子序列中的數都處理完了,另一個還沒有,把剩下的複製過來 28 while(i<=mid) 29 b[t++]=a[i++]; 30 while(j<=R) 31 b[t++]=a[j++]; 32 //陣列拷貝 33 for(int i=0;i<t;i++) 34 a[L+i]=b[i]; 35 } 36 void mergesort(int low,int high) 37 { 38 if(low<high) 39 { 40 int mid=(low+high)/2; 41 //分 42 mergesort(low,mid); 43 mergesort(mid+1,high); 44 //治 45 Merge(low,mid,high); 46 } 47 } 48 signed main() 49 { 50 IOS; 51 while(cin>>n>>k) 52 { 53 cnt=0; 54 for(int i=0;i<n;i++) 55 { 56 cin>>a[i]; 57 } 58 mergesort(0,n-1); 59 if(cnt<=k) 60 cout<<0<<endl; 61 else 62 cout<<cnt-k<<endl; 63 } 64 }