ural1090 逆序對(歸併排序和樹狀陣列)
阿新 • • 發佈:2019-01-03
題意:
在軍隊中
軍官命令新兵排行成列。新兵們排成了K行,每行有N人。但有K人沒有排在正確的位置上。
正確的排隊位置如下:第一個士兵必須是最高的,第二個是第二高的,依此類推,最後一個士兵必須是最矮的。為了排好隊,軍官規定每一個士兵,如果與他同一排的前一個人比他矮,那麼他就向前跳一步。
注意沒有兩個新兵的身高相同。
軍官想找出哪一排士兵跳的總次數最多,好懲罰他們到廚房去工作。你的目標就是幫助軍官找到這一排。
Input
輸入的第一行包含了兩個數N和K(2≤N≤10000,1≤K≤20)。接下來的K行每行包含N個整數。新兵已經按照身高編好了號(1號最高,N號最矮)。每一行是相應的一排,例如某一行的第一個整數代表這行的第一個人等等。
Output
輸出跳躍次數最多的那一行的編號。如果有幾行次數相同,則輸出行編號最小的那個。
是在個人訓練賽的時候遇到的一道簽到題,求逆序對如果列舉的話時間複雜度是n^2,太大了。一般我們用於求逆序對的方法有兩種,一種是歸併排序,一種是樹狀陣列。歸併排序的程式碼如下:
#include <cstdio> #include <iostream> #include <algorithm> #include <cstring> using namespace std; const int maxn=10000+20; const int maxk=25; int G[maxk][maxn]; int n,k,num ; int t[maxn]; int a[maxn]; void merge_sort(int *A,int L,int R,int *T){ if(R-L==0)return ; int m=L+(R-L)/2; merge_sort(A,L,m,T); merge_sort(A,m+1,R,T); int p=L,q=m+1,i=L; while(p<=m||q<=R){ if((A[p]<A[q]&&p<=m)||q>R)T[i++]=A[p++]; else{ T[i++]=A[q++]; num+=m-p+1; } } for(int i=L;i<=R;i++)A[i]=T[i]; } int main(){ cin>>n>>k; int ans=-1,ansn=-1; for(int i=1;i<=k;i++){ for(int j=1;j<=n;j++){ cin>>G[i][j]; } num=0; merge_sort(G[i],1,n,t); if(ans<num){ ans=num;ansn=i; } } cout<<ansn; return 0; }
這種方法是本來就會的一種方法,很快寫完A掉了,但是從來沒有嘗試過用樹狀陣列來求逆序對。一開始思維受限於歸併的方法,一直在考慮樹狀數組裡存的內容是什麼,後來想到,直接把每個數字樹狀陣列作為下標對於每個數字a,通過樹狀陣列求得比a小的數字有多少。但是發現,因為要直接以每個數字a的值作為樹狀陣列的下標,所以···開不下,於是想了另一種辦法,用一個結構體來儲存每個數字的值,並給他們重新排序,給每個值賦予一個編號mval,mval的範圍位1-n,這樣便可以解決此問題了(查資料瞭解到這個方法叫離散化,而我離散化的寫法非常白痴
#include <cstdio> #include <cstring> #include <algorithm> #include <iostream> using namespace std; const int maxn=10000+10; const int maxk=25; struct Num{ int val,mval,i; }num[maxn]; int G[maxn],C[maxn] ; int n,k; int lowbit(int x){return x&-x;} int sum(int x){ int ans=0; while(x>0){ ans+=C[x]; x-=lowbit(x); } return ans; } int add(int x){ while(x<=n){ C[x]+=1; x+=lowbit(x); } } int cmp1(Num a,Num b){ return a.val<b.val; } int cmp2(Num a,Num b){ return a.i<b.i; } int main(){ cin>>n>>k; int ans=-1,ansn=-1; for(int i=1;i<=k;i++){ int ANS=0; memset(C,0,sizeof(C)); for(int j=1;j<=n;j++){ cin>>num[j].val; num[j].i=j; } sort(num+1,num+1+n,cmp1); for(int j=1;j<=n;j++) num[j].mval=j; sort(num+1,num+1+n,cmp2); for(int j=1;j<=n;j++){ int a=num[j].mval; add(a); ANS+=(j-sum(a)); } /*cin>>G[i][j]; add(G[i][j]); ANS+=(j-sum(G[i][j]));*/ //cout<<num<<endl; if(ans<ANS){ ans=ANS,ansn=i; } } cout<<ansn; return 0; }
)