逆序数2 HDOJ 1394 Minimum Inversion Number

题目传送门

1 /*
2     求逆序数的四种方法
3 */

 1 /*
 2     1. O(n^2) 暴力+递推 法:如果求出第一种情况的逆序列,其他的可以通过递推来搞出来,一开始是t[1],t[2],t[3]....t[N]
 3     它的逆序列个数是N个,如果把t[1]放到t[N]后面,逆序列个数会减少t[1]个,相应会增加N-(t[1]+1)个
 4 */
 5 #include <cstdio>
 6 #include <cstring>
 7 #include <algorithm>
 8 using namespace std;
 9
10 const int MAX_N = 10000 + 10;
11 const int INF = 0x3f3f3f3f;
12 int a[MAX_N];
13 int num[MAX_N];
14
15 int main(void)      //HDOJ 1394 Minimum Inversion Number
16 {
17     //freopen ("inC.txt", "r", stdin);
18     int n;
19
20     while (~scanf ("%d", &n))
21     {
22         memset (num, 0, sizeof (num));
23         for (int i=1; i<=n; ++i)
24         {
25             scanf ("%d", &a[i]);
26             //a[n+i] = a[i];
27         }
28         int t = 0;  int sum = 0;
29         for (int i=1; i<=n; ++i)    //先求解最初的数列逆序数
30         {
31             for (int j=i+1; j<=n; ++j)
32             {
33                 if (a[i] > a[j])
34                 {
35                     sum++;
36                 }
37             }
38         }
39         //printf ("%d\n", sum);
40         int ans = INF;
41         for (int i=1; i<=n; ++i)        //更新sum,找最小
42         {
43             sum = sum - a[i] + (n - a[i] - 1);      //the next line contains a permutation of the n integers from 0 to n-1.
44             //printf ("%d\n", res);                 //从0到n-1的整数  所以这里用a[i]  读题不仔细 。。。。
45             ans = min (sum, ans);
46         }
47         printf ("%d\n", ans);
48     }
49
50     return 0;
51 }

O(n^2) 暴力+递推

nlogn归并算法

 1 /*
 2     3. nlogn 线段树-单点更新:更新比a[i]大的个数
 3 */
 4 #include <cstdio>
 5 #include <algorithm>
 6 #define lson l, m, rt << 1
 7 #define rson m+1, r, rt << 1 | 1
 8
 9 const int MAX_N = 5000 + 10;
10 const int INF = 0x3f3f3f3f;
11 int a[MAX_N];
12 int sum[MAX_N << 2];
13
14 void pushup(int rt)
15 {
16     sum[rt] = sum[rt << 1] + sum[rt << 1 | 1];
17 }
18
19 void build(int l, int r, int rt)
20 {
21     sum[rt] = 0;
22     if (l == r) return ;
23     int m = (l + r) >> 1;
24     build (lson);
25     build (rson);
26 }
27
28 void update(int p, int l, int r, int rt)
29 {
30     if (l == r)
31     {
32         sum[rt]++;      //记录次数
33         return ;
34     }
35     int m = (l + r) >> 1;
36     if (p <= m)
37     {
38         update (p, lson);
39     }
40     else
41         update(p, rson);
42     pushup (rt);
43 }
44
45 int query(int ql, int qr, int l, int r, int rt)
46 {
47     if (ql <= l && r <= qr)
48     {
49         return sum[rt];
50     }
51     int m = (l + r) >> 1;
52     int ans = 0;
53     if (ql <= m)    ans += query (ql, qr, lson);
54     if (qr > m)     ans += query (ql, qr, rson);
55
56     return ans;
57 }
58
59 int main(void)      //HDOJ 1394 Minimum Inversion Number
60 {
61     //freopen ("inC.txt", "r", stdin);
62     int n;
63     while (~scanf ("%d", &n))
64     {
65         //memset (num, 0, sizeof (num));
66         build (0, n-1, 1);
67         int sum = 0;
68         for (int i=1; i<=n; ++i)
69         {
70             scanf ("%d", &a[i]);
71             sum += query (a[i], n-1, 0, n-1, 1);
72             update (a[i], 0, n-1, 1);
73         }
74         int ans = sum;
75         for (int i=1; i<=n; ++i)
76         {
77             sum = sum - a[i] + (n - a[i] - 1);
78             ans = std::min (sum, ans);
79         }
80         printf ("%d\n", ans);
81     }
82
83     return 0;
84 }

nlogn 线段树-单点更新

 1 /*
 2     4. 树状数组
 3 */
 4 #include <stdio.h>
 5 #include <string.h>
 6 #include <algorithm>
 7 using namespace std;
 8 const int MAXN=5050;
 9 int c[MAXN];
10 int a[MAXN];
11 int n;
12
13 int lowbit(int x)
14 {
15     return x&(-x);
16 }
17
18 void add(int i,int val)
19 {
20     while(i<=n)
21     {
22         c[i]+=val;
23         i+=lowbit(i);
24     }
25 }
26
27 int sum(int i)
28 {
29     int s=0;
30     while(i>0)
31     {
32         s+=c[i];
33         i-=lowbit(i);
34     }
35     return s;
36 }
37
38 int main()      //HDOJ 1394 Minimum Inversion Number
39 {
40     //freopen ("inC.txt", "r", stdin);
41     while(scanf("%d",&n)!=EOF)
42     {
43         int ans=0;
44         memset(c,0,sizeof(c));
45         for(int i=1;i<=n;i++)
46         {
47             scanf("%d",&a[i]);
48             a[i]++;
49             ans+=sum(n)-sum(a[i]);
50             add(a[i],1);
51         }
52         int Min=ans;
53         for(int i=1;i<=n;i++)
54         {
55             ans+=n-a[i]-(a[i]-1);
56             if(ans<Min)Min=ans;
57         }
58         printf("%d\n",Min);
59     }
60
61     return 0;
62 }

树状数组

时间: 2024-10-12 03:24:17

逆序数2 HDOJ 1394 Minimum Inversion Number的相关文章

hdoj 1394 Minimum Inversion Number【线段树求逆序对】

求逆序对有很多算法,这里说一下线段树求逆序对的思想. 知识点:线段树,逆序对,单点更新,成段求和 算法:线段树求逆序数的前提条件是要离散化,变成连续的点,首先建树,每个节点设置一个num值为0. 然后根据逆序对的定义,前面出现过的比当前数大的个数的和,我们需要求前面的比他大的数,其实就相当于从当前a[i]点对他后面所有出现过的数求和一次.然后把当前点的值在线段树叶子节点变为1,表示出现过,并向上更新到线段树里面.比如说样例4 2 1 5 3 首先4后面没有值,4更新为1,4--5区间更新为1,1

hdoj 1394 Minimum Inversion Number 【线段数】

题目大意:求移动数列中的第一个元素到最后一位时的最少逆序数.(进行n次移动,求移动过程中最少的逆序数) 难点: 一:什么是逆序数? 定义: 在一个排列中,如果一对数的前后位置与大小顺序相反,即前面的数大于后面的数,那么它们就称为一个逆序.一个排列中逆序的总数就称为这个排列的逆序数.逆序数为偶数的排列称为偶排列:逆序数为奇数的排列称为奇排列.如2431中,21,43,41,31是逆序,逆序数是4,为偶排列. 二:怎么求? 此题分析一下有个技巧:对于这道题因为是0~n-1所以我们可以通过下标就可以判

HDOJ 1394 Minimum Inversion Number 求循环串的最小逆序数(暴力&amp;&amp;线段树)

Minimum Inversion Number Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Total Submission(s): 14879    Accepted Submission(s): 9082 Problem Description The inversion number of a given number sequence a1, a2, ..., a

hdu 1394 Minimum Inversion Number (裸树状数组 求逆序数)

题目链接 题意: 给一个n个数的序列a1, a2, ..., an ,这些数的范围是0-n-1, 可以把前面m个数移动到后面去,形成新序列:a1, a2, ..., an-1, an (where m = 0 - the initial seqence)a2, a3, ..., an, a1 (where m = 1)a3, a4, ..., an, a1, a2 (where m = 2)...an, a1, a2, ..., an-1 (where m = n-1)求这些序列中,逆序数最少的

HDU 1394 Minimum Inversion Number(线段树求逆序数)

题目地址:HDU 1394 这题可以用线段树来求逆序数. 这题的维护信息为每个数是否已经出现.每次输入后,都从该点的值到n-1进行查询,每次发现出现了一个数,由于是从该数的后面开始找的,这个数肯定是比该数大的.那就是一对逆序数,然后逆序数+1.最后求完所有的逆序数之后,剩下的就可以递推出来了.因为假如目前的第一个数是x,那当把他放到最后面的时候,少的逆序数是本来后面比他小的数的个数.多的逆序数就是放到后面后前面比他大的数的个数.因为所有数都是从0到n-1.所以比他小的数就是x,比他大的数就是n-

HDU 1394 Minimum Inversion Number(线段树求最小逆序数对)

HDU 1394 Minimum Inversion Number(线段树求最小逆序数对) ACM 题目地址:HDU 1394 Minimum Inversion Number 题意: 给一个序列由[1,N]构成,可以通过旋转把第一个移动到最后一个. 问旋转后最小的逆序数对. 分析: 注意,序列是由[1,N]构成的,我们模拟下旋转,总的逆序数对会有规律的变化. 求出初始的逆序数对再循环一遍就行了. 至于求逆序数对,我以前用归并排序解过这道题:点这里. 不过由于数据范围是5000,所以完全可以用线

HDU 1394——Minimum Inversion Number(最小逆序数)

题意: 给定一个序列,里面的数是0到n-1,  每次要把第一个数放到最后一个数,重复n次,求n次操作中最小的逆序数是多少? 思路: 先求出初始的逆序数,然后每移动第一个数到最后面,那么逆序数要减去比它小的数的个数,加上比它大的数的个数. 如果我输入的数是a[i],那么比它小的数的个数就有a[i]个,比它大的数的个数就有n-1-a[i]个 方法:    归并排序 ,树状数组,线段树 归并排序代码: #include<iostream> #include<cstring> #inclu

HDU 1394 Minimum Inversion Number(逆序数)

题目链接:HDU 1394 Minimum Inversion Number [题意]给你一个1~N的数字组成的初始序列,然后每一次都将第一个数字移到最后,形成新的序列,然后求出这些序列的逆序数中的最小值. [思路]开始可以用任意一种方法(线段树 or 暴力 or 树状数组)计算出初始数列的逆序数sum,这里我比较懒,就直接用的暴力找的sum,对于a[i](0~~n-1),每挪一个,用sum减去挪之前它右边比它小的数的个数(也就是a[i]个),再用sum加上挪之后左边比它大的数的个数(也就是n-

HDU 1394 Minimum Inversion Number(线段树:单点更新,求逆序数)

http://acm.hdu.edu.cn/showproblem.php?pid=1394 Minimum Inversion Number Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Total Submission(s): 14648    Accepted Submission(s): 8942 Problem Description The inversion n