timus[1090. In the Army Now]
看了一下题目的意思,求逆序数,求出逆序数最大的行号。
求逆序数是经典问题,想起当年刚学编程时,算法导论里就有这个习题,如何将merge sort修改为可以计算逆序数的版本,于是写了一下,得到了以下一份答案:
1 #include <iostream> 2 #include <algorithm> 3 4 using namespace std; 5 6 const int MAX_N = 10000; 7 8 const int INF = MAX_N + 1; 9 10 int N, K; 11 12 int A[MAX_N]; 13 14 int aux[2][(MAX_N >> 1) + 1]; 15 16 int merge(int p, int mid, int q) 17 { 18 19 int left_length = mid + 1 - p; 20 int right_length = q - mid; 21 for (int i = 0; i < left_length; ++i) 22 { 23 aux[0][i] = A[p + i]; 24 } 25 aux[0][left_length] = INF; 26 27 for (int i = 0; i < right_length; ++i) 28 { 29 aux[1][i] = A[mid + 1 + i]; 30 } 31 aux[1][right_length] = INF; 32 int ans = 0; 33 int i = 0; 34 int j = 0; 35 for (int k = 0; k < left_length + right_length; ++k) 36 { 37 if (aux[0][i] < aux[1][j]) 38 { 39 A[p + k] = aux[0][i++]; 40 } 41 else 42 { 43 A[p + k] = aux[1][j++]; 44 ans += left_length - i; 45 } 46 } 47 return ans; 48 } 49 50 int inversions(int p, int q) 51 { 52 int ans = 0; 53 if (p < q) 54 { 55 int mid = (p + q) / 2; 56 ans += inversions(p, mid); 57 ans += inversions(mid + 1, q); 58 ans += merge(p, mid, q); 59 } 60 return ans; 61 } 62 63 int main() 64 { 65 cin >> N >> K; 66 int ans; 67 int max_inversions = -1; 68 for (int k = 0; k < K; ++k) 69 { 70 for (int i = 0; i < N; ++i) 71 { 72 cin >> A[i]; 73 } 74 int x = inversions(0, N - 1); 75 if (x > max_inversions) 76 { 77 max_inversions = x; 78 ans = k + 1; 79 } 80 } 81 cout << ans << endl; 82 83 return 0; 84 }
发现这样算用了0.28s的时间,于是,换成另一个经典解法,树状数组(binary indexed tree)试试呢,于是有了下面这份答案:
#define _CRT_SECURE_NO_WARNINGS #include <stdio.h> #include <string.h> using namespace std; const int MAX_N = 10000; const int INF = MAX_N + 1; int N, K; int A[MAX_N + 1]; void add(int x, int index) { /* * Actually in this problem, we don't have 0 as index at all, * and x is alway 1. */ if (index == 0) { A[0] += x; return; } while (index <= N) { A[index] += x; index += index & -index; } } int prefix_sum(int x) { int ans = A[0]; while (x) { ans += A[x]; x &= x - 1; } return ans; } int main() { scanf("%d%d", &N, &K); int ans; int max_inversions = -1; for (int k = 0; k < K; ++k) { memset(A, 0, sizeof(int) * (N + 1)); int sum = 0; for (int i = 0; i < N; ++i) { int x; scanf("%d", &x); x = N + 1 - x; sum += prefix_sum(x - 1); add(1, x); } if (sum > max_inversions) { max_inversions = sum; ans = k + 1; } } printf("%d\n", ans); return 0; }
跑了一下,用时0.046s,已经比merge sort的版本快挺多了。
由于实在无聊,那么用线段树试一下呢,于是改成以下的代码:
#define _CRT_SECURE_NO_WARNINGS #include <stdio.h> #include <string.h> const int SIZE = 16384; int N, K; int A[SIZE << 1]; void add(int x, int index) { index += SIZE; while (index != 0) { A[index] += x; index >>= 1; } } int prefix_sum(int x) { int ans = 0; int index = 1; int left = 0; int right = SIZE - 1; while (left <= x) { if (right == x) { ans += A[index]; left = x + 1; } else { int mid = (left + right) >> 1; if (mid >= x) { index <<= 1; right = mid; } else { ans += A[index << 1]; left = mid + 1; index = (index << 1) + 1; } } } return ans; } int main() { scanf("%d%d", &N, &K); int ans; int max_inversions = -1; for (int k = 0; k < K; ++k) { memset(A, 0, sizeof(A)); int sum = 0; for (int i = 0; i < N; ++i) { int x; scanf("%d", &x); x = N + 1 - x; sum += prefix_sum(x - 1); add(1, x); } if (sum > max_inversions) { max_inversions = sum; ans = k + 1; } } printf("%d\n", ans); return 0; }
这份代码耗时0.062s,竟然比树状数组的还慢?

浙公网安备 33010602011771号