hdu 1394 Minimum Inversion Number

题目链接:hdu 1394 Minimum Inversion Number

该题是求最小逆序对的扩展。可以使用树状数组来实现。对于$n$个数的序列$A$,其第$i$个数($i\in [0,n)$)的逆序数$r_i$可以表示为它的角标$i$减去在它之前且不大于它的数的个数。例如对序列A = {1,3,5,9,0,8,5,7,4,2}中的数,A[8] = 4。其逆序数$r_8 = 8 - 3 = 5$,第二个3表示三个在它前面且比它小的数:{1,3,0}。从而我们可以得到第$i$个数的逆序数公式:

\begin{equation} r_i = i - \sum_{0 \leq j < i And A_j \leq A_i } 1 \end{equation}

对于整个序列$A$有

\begin{equation} R = \sum_{0 \leq i < n}r_i \end{equation}

可以使用树状数组的前i项的和sum(i)函数来快速的计算$\sum_{0 \leq j < i And A_j \leq A_i } 1$,并且使用add(i+1)来对该数进行计数。

得到初始数列的逆序数后,可以轻松地计算变换后序列的逆序数。

如果删除掉第一个数$A_1$,那么逆序数会减小$A_1$。

在把第一个数放到数列结尾,那么逆序数又会增加$n-A_1-1$

从而可以尝试所有的变换方法,最终取最小值即可。

代码如下:

 1 #include <cstdlib>
 2 #include <cstdio>
 3 #include <iostream>
 4 #include <cstring>
 5 #define MAXN  5005
 6 using namespace std;
 7 int bit[MAXN];
 8 int arr[MAXN];
 9 int n;
10 int lowbit(int x)
11 {
12     return x&(-x);
13 }
14 int sum(int x)
15 {
16     int ans = 0;
17     while( x > 0 )
18     {
19         ans += bit[x];
20         x -= lowbit(x);
21     }
22     return ans;
23 }
24 void add(int l, int x)
25 {
26     while( l <= n )
27     {
28         bit[l] += x;
29         l += lowbit(l);
30     }
31 }
32 int main(int argc, char *argv[])
33 {
34     while( scanf("%d", &n) != EOF )
35     {
36         int ans = 0;
37         memset(bit, 0, sizeof(bit));
38         for( int i = 0 ; i < n ; i++ )
39         {
40             scanf("%d", &arr[i]);
41             ans += i - sum(arr[i]+1);
42             add(arr[i]+1, 1);
43         }
44         int mi = ans;
45         for( int i = 0 ; i < n ; i++ )
46         {
47             ans = ans - arr[i] + n - arr[i] -1;
48             mi = ans > mi ? mi : ans;
49         }
50         printf("%d\n", mi);
51     }
52 }

 

posted @ 2014-10-02 22:44  jostree  阅读(158)  评论(0编辑  收藏  举报