timus[1090. In the Army Now]

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 }
Solution with merge sort.

 发现这样算用了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;
}
Solution with binary indexed tree.

跑了一下,用时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;
}
Solution with segment tree.

这份代码耗时0.062s,竟然比树状数组的还慢?

posted @ 2018-11-11 16:31  knull  Views(244)  Comments(0)    收藏  举报