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 }
目前求逆序对数目比较普遍的方法是利用归并排序做到的时间复杂度。 当然,也可以利用树状数组、线段树来实现这种基础功能。复杂度均为。
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 }