POJ2299Ultra-QuickSort (線段樹和歸並排序的解法)


題目大意就是說幫你給一些(n個)亂序的數讓你求冒泡排序需要交換數的次數(n<=500000)

此題最初真不會做,我也只是在聽了章爺的講解后才慢慢明白過來的

首先介紹線段樹的解法:

我們先將原數組每個值附上一個序號index,再將它排序。如題目的例子:

num:   9  1  0  5  4

index:  1  2  3  4  5

排序后:

num:   0  1  4  5  9

index:  3  2  5  4  1

然后由於排序后num為0的點排在原來數組的第3個,所以為了將它排到第一個去,那就至少需要向前移動兩次,同時它也等價於最小的數0之前有2個數比它大(所以要移動兩次),將0移到它自己的位置后,我們將0刪掉(目的是為了不對后面產生影響)。再看第二大的數1,它出現在原數組的第二個,他之前有一個數比它大所以需要移動一次。這樣一直循環下去那么着5個數所需要移動的次數就是:

num:  0  1  4  5  9

次數      2  1  2  1  0

將次數全部要加起來就是最后所需要移動的總次數。

方法章爺是已經告訴我了,但是最初我一直是覺得不好實現。到后來才慢慢、慢慢弄好。方法就是在建一棵樹時,不是直接將原來的num放進樹里面,而是將它的下標放進樹里面,最初每個節點上賦值為1.然后當查找第一個num時,由於是找的下標為3的位置,所以我們就直接找區間[1,3)之間有多少個1(就是求前導和),這里面1的個數就是第一個num=0索要移動的次數,然后我們把0去掉,其實也就是吧下標為3的那個1去掉。這樣每個值就依次計算出來了。

當然其實只要是想明白了,不用線段樹,直接用樹狀數組寫起來會簡便很多。(因為每次只需要計算前導和以及去掉某一個點,是對點的操作)。

 

這里再講一下歸並排序的方法(對於最基礎就沒有掌握好的我來說聽到他們說歸並排序可以解題時,我竟然一團霧水,竟然連歸並排序都忘記了),看了一下歸並排序的實現過程,其實馬上就可以找到思路,由於本題實際上就是要求逆序對(即滿足i<j,a[i]>a[j]的數對)的個數。而我們再回顧一下歸並排序的過程:

假設回溯到某一步,后面的兩部分已經排好序(就是說當前需要歸並的兩個部分都是分別有序的),假設這兩個序列為

序列a1:2 3 5 9  

序列a2:1 4 6 8

此時我們的目的就是要將a1和a2合並為一個序列。

由於在沒排序前a2序列一定全部都是在a1序列之后的,當我們比較a2的1與a1的2時,發現1<2按照歸並的思想就會先記錄下a2的1,而這里實際上就是對冒泡排序的優化,冒泡是將a2的1依次與a1的9,5,3,2交換就需要4次,而歸並卻只有一次就完成了,要怎么去記錄這個4呢,實際上由於1比2小而2后面還有4個數,也就是說那我的結果就必須要+4,也就是記錄a1序列找到第一個比a2某一個大的數,他后面還余下的數的個數就是要交換的次數。

同時我們看a2的4時,a1中第一個比它大的數是5,5之后共有兩個數,那結果就+2,。依次下去就可以計算出結果。但是由於我們任然沒有改變歸並排序的過程。所以復雜度還是O(nlogn),比上面的線段樹要快。

另外,此題有一坑就是結果會超int32,,用__int64

首先是線段樹的解法11892 KB   1047 ms

 1 #include <stdio.h>
 2 #include <string.h>
 3 #include <algorithm>
 4 #define mem(a) memset(a,0,sizeof(a))
 5 #define MIN(a , b) ((a) < (b) ? (a) : (b))
 6 #define MAXN 500010
 7 #define INF  1000000007
 8 #define lson k<<1,     l,     mid
 9 #define rson (k<<1)|1, mid+1,  r
10 
11 using namespace std;
12 
13 int Tree[MAXN<<2], index[MAXN], num[MAXN];
14 int N;
15 
16 int cmp(const int i, const int j)
17 {
18     return num[i] < num[j];
19 }
20 
21 void Edit(int k, int l, int r, int num, int value)
22 {
23     Tree[k] += value;
24     if(r==l) return ;
25     int mid = (l+r) >> 1;
26     if(num <= mid) Edit(lson, num, value);
27     else Edit(rson, num, value);
28 }
29 
30 int Search(int k, int l, int r, int L, int R)//L,R是要找的區間
31 {
32     if(L<=l && r<=R) return Tree[k];
33     int mid = (l+r) >> 1;
34     int ans = 0;
35     if(L <= mid) ans += Search(lson, L, R);
36     if(R > mid) ans += Search(rson, L, R);
37     return ans;
38 }
39 
40 int main()
41 {
42     while(~scanf("%d", &N) && N)
43     {
44         mem(Tree); mem(num); mem(index);
45         for(int i=1;i<=N;i++)
46         {
47             scanf("%d", &num[i]);
48             Edit(1, 1, N, i, 1);
49             index[i] = i;
50         }
51         sort(index+1, index+N+1,cmp);
52         long long ans = 0;
53         for(int i=1;i<=N;i++)
54         {
55             ans += (Search(1, 1, N, 1, index[i])-1);
56             Edit(1, 1, N, index[i], -1);
57         }
58         printf("%I64d\n", ans);
59     }
60     return 0;
61 }

然后是歸並排序(只有注釋位置有改動,其他的無變化)4076K   438MS

 1 #include <stdio.h>
 2 #include <string.h>
 3 #define mem(a) memset(a, 0, sizeof(a))
 4 
 5 int N, A[500010], T[500010];
 6 __int64 ans;
 7 
 8 void Merg_Sort(int x,int y)
 9 {
10     if(y-x<=1) return ;
11     int mid = x + (y-x)/2;
12     Merg_Sort(x,mid);
13     Merg_Sort(mid,y);
14     int p = x, q = mid, i=x;
15     while(p<mid || q<y)
16     {
17         if(q>=y || (p<mid && A[p] <= A[q])) T[i++] = A[p++];
18         else//else的條件是(p==mid || A[q] < A[p])
19         {
20             if(p<mid) ans+=(mid-p);//由於是p<mid,所以此時也就是相當於 A[q] < A[p]
21             T[i++] = A[q++];       //上面同時A[p]是第一個<A[q]的數,所以+后面還有的數(mid-p)
22         }
23     }
24     for(i=x;i<y;i++)
25     {
26         A[i] = T[i];
27     }
28 }
29 
30 int main()
31 {
32     while(~scanf("%d", &N) && N)
33     {
34         mem(A); mem(T);
35         for(int i=0;i<N;i++)
36         {
37             scanf("%d", &A[i]);
38         }
39         ans = 0;
40         Merg_Sort(0,N);
41         printf("%I64d\n",ans);//結果會超int32
42     }
43     return 0;
44 }

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM