uva 10810 - Ultra-QuickSort

题意为,给你一个序列, 每次交换两个相邻的数使序列为递增的序列, 求最小的交换次数。

 

首先我们可以看出。 最少的交换次数肯定得用归并排序来求了。

实际上归并排序的交换次数就是这个数组的逆序对个数,为什么呢?

我们可以这样考虑:

归并排序是将数列a[l,h]分成两半a[l,mid]和a[mid+1,h]分别进行归并排序,然后再将这两半合并起来。

在合并的过程中(设l<=i<=mid,mid+1<=j<=h),当a[i]<=a[j]时,并不产生逆序数;当a[i]>a[j]时,在

前半部分中比a[i]大的数都比a[j]大,将a[j]放在a[i]前面的话,逆序数要加上mid+1-i。因此,可以在归并

排序中的合并过程中计算逆序数.

 1 #include <cstdio>
 2 #include <algorithm>
 3 #include <iostream>
 4 #include <cstring>
 5 #include <cmath>
 6 #include <cstdlib>
 7 #include <string>
 8 #include <map>
 9 #include <vector>
10 #include <set>
11 #include <queue>
12 #include <stack>
13 #include <cctype>
14 using namespace std;
15 typedef long long LL;
16 typedef unsigned long long ULL;
17 #define MAXN 500000+10
18 #define INF (1<<30)
19 #define mod 123456789
20 int a[MAXN];
21 int tem[MAXN];
22 LL total = 0;
23 int Merge_sort(int l, int r){
24     int mid = (l+r)>>1;
25     int x = l, y = mid+1;
26     int ans = 0;
27     while(x <= mid && y <= r){
28         if(a[x] > a[y]){
29             tem[ans++] = a[y];
30             y++;
31             total += mid-x+1;
32         }
33         else {
34             tem[ans++] = a[x];
35             x++;
36         }
37     }
38     while(x <= mid) tem[ans++] = a[x++];
39     while(y <= r)   tem[ans++] = a[y++];
40     ans = 0;
41     for(int i = l; i <= r; i++)
42         a[i] = tem[ans++];
43 }
44 int Merge(int l,int r){
45     if(l < r){
46         int mid = (l+r)>>1;
47         Merge(l,mid);
48         Merge(mid+1,r);
49         Merge_sort(l,r);
50     }
51 }
52 int main (){
53     int n;
54     while(scanf("%d",&n) != EOF && n){
55         total = 0;
56         for(int i = 0; i < n; i++){
57             scanf("%d",&a[i]);
58         }
59         Merge(0,n-1);
60         cout << total << endl;
61     }
62     return 0;
63 }

来源

目前求逆序对数目比较普遍的方法是利用归并排序做到O(n \log n)的时间复杂度。 当然,也可以利用树状数组、线段树来实现这种基础功能。复杂度均为O(n \log n)

 1 #include <stdio.h>
 2 #define inf 2000000000
 3 #define sz 500005
 4 
 5 void mergesort(int p, int r);
 6 void merge(int p, int q, int r);
 7 int a[sz], L[(sz/2)+5], R[(sz/2)+5];
 8 long long int cnt;
 9 
10 int main()
11 {
12     int n, i;
13     while(scanf("%d", &n) && n)
14     {
15         for(i = 1; i <= n; i++)
16             scanf("%d", &a[i]);
17         cnt = 0;
18         mergesort(1, n);
19         printf("%lld\n", cnt);
20     }
21     return 0;
22 }
23 
24 void mergesort(int p, int r)
25 {
26     if(p < r)
27     {
28         int q;
29         q = (p+r)/2;
30         mergesort(p, q);
31         mergesort(q+1, r);
32         merge(p, q, r);
33     }
34     return ;
35 }
36 
37 void merge(int p, int q, int r)
38 {
39     int ind1, ind2, k, i, j;
40     for(i = p, ind1 = 1; i <= q; i++)
41         L[ind1++] = a[i];
42     L[ind1] = inf;
43     for(i = q + 1, ind2 = 1; i <= r; i++)
44         R[ind2++] = a[i];
45     R[ind2] = inf;
46     i = j = 1;
47     for(k = p; k <= r; k++)
48     {
49         if(L[i] > R[j])
50         {
51             cnt += ind1 - i;
52             a[k] = R[j];
53             j++;
54         }
55         else
56         {
57             a[k] = L[i];
58             i++;
59         }
60     }
61     return ;
62 }

 

 1 /*
 2  *  Problem : "Ultra-QuickSort"
 3  *  ID      : 10810
 4  *  Date    : 2011-03-15
 5  *  Idea    : 這題可以學到很多東西,雖然題目是叫 Quick Sort ,但是它卻要求我們要記錄 swap 的次數,
 6  *            因此第一時間想到的就是 10327 題的 Flip Sort (即 Bubble Sort),不過這題是其進階版,
 7  *            最大的問題就是要克服 TLE 的問題( O(n^2) 一定會超過 3 秒),所以我們從 Merge Sort 下手
 8  *            然後發現 Merge Sort 其 Left Array & Right Array 之間其實存在一個很奇妙的關係,要記錄
 9  *            Swap 次數是要在 L > R 的時候才需要,而且公式就是 n1 - i + 1(可在紙上演練一下)。
10  *
11  *            另外要注意一下就是 count 可能 overflow ,記得 long long !
12  *  Author  : EragonJ
13  */
14 #include <iostream>
15 #define MAX 502000
16 using namespace std;
17 
18 long long int count = 0;
19 
20 void merge(int A[], int p, int q, int r) {
21   extern long long int count;
22 
23   int n1 = q - p + 1;
24   int n2 = r - q;
25 
26   int* L = (int*) malloc((n1 + 1) * sizeof(int));
27   int* R = (int*) malloc((n2 + 1) * sizeof(int));
28 
29   for (int i = 1; i <= n1; i++) {
30     L[i] = A[p + i - 1];
31   }
32 
33   for (int i = 1; i <= n2; i++) {
34     R[i] = A[q + i];
35   }
36 
37   int i, j, k;
38   for (k = p, i = 1, j = 1; (k <= r) && (i <= n1) && (j <= n2); k++) {
39     if (L[i] <= R[j]) {
40       A[k] = L[i];
41       i++;
42     }
43     else {
44       A[k] = R[j];
45       j++;
46       count += (n1 - i + 1); // Important formula
47     }
48   }
49 
50   for (i; i <= n1; i++) {
51     A[k++] = L[i]; 
52   }
53 
54   for (j; j <= n2; j++) {
55     A[k++] = R[j];
56   }
57 }
58 
59 void merge_sort(int A[], int p, int r) {
60   if (p < r) {
61     int q = (p + r) / 2;
62     merge_sort(A, p, q); 
63     merge_sort(A, q + 1, r);
64     merge(A, p, q, r);
65   }
66 }
67 
68 int main() {
69 
70   int n;
71   int input[MAX] = {0};
72   while (scanf("%d", &n) == 1) {
73     if (n == 0) {
74       break;
75     }
76 
77     for (int i = 0; i < n; i++) {
78       // Index range : 1 ~ n;
79       scanf("%d", &input[i + 1]);
80     }
81 
82     merge_sort(input, 1, n);
83 
84     extern long long int count;
85     cout << count << endl;
86 
87     count = 0;
88   }
89 
90   return 0;
91 }

 

解题方法:离散化+树状数组

 

 1 /*题目大意:给出一个序列,每次交换两个数,这两个数之间的距离就是代价,问说要将序列排序的总代价是多少。
 2 解题思路:归并排序下的逆序数的个数。*/
 3 #include <stdio.h>
 4 #include <string.h>
 5 
 6 const int N = 500005;
 7 typedef long long ll;
 8 int n, g[N], f[N];
 9 
10 ll Msort(int l, int r, int* a, int* b) {
11     if (r - l == 1) return 0;
12 
13     int m = (l + r) / 2;
14     ll ans = Msort(l, m, a, b) + Msort(m, r, a, b);
15     int p = l, q = m, c = l;
16     while (p < m || q < r) {
17         if (q >= r || (p < m &&  a[p] <= a[q])) b[c++] = a[p++];
18         else {
19             ans += m - p;
20             b[c++] = a[q++];
21         }
22     }
23     for (int i = l; i < r; i++) a[i] = b[i];
24     return ans;
25 }
26 
27 int main() {
28     while (scanf("%d", &n) == 1 && n) {
29         for (int i = 0; i < n; i++) scanf("%d", &g[i]);
30         printf("%lld\n", Msort(0, n, g, f));
31     }
32     return 0;
33 }

 

posted @ 2016-01-25 18:36  小小泽  阅读(501)  评论(0编辑  收藏  举报