Minimum Inversion Number
Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others)
Total Submission(s): 11981 Accepted Submission(s): 7321
Problem Description
The inversion number of a given number sequence a1, a2, ..., an is the number of pairs (ai, aj) that satisfy i < j and ai > aj.
For a given sequence of numbers a1, a2, ..., an, if we move the first m >= 0 numbers to the end of the seqence, we will obtain another sequence. There are totally n such sequences as the following:
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)
You are asked to write a program to find the minimum inversion number out of the above sequences.
Input
The input consists of a number of test cases. Each case consists of two lines: the first line contains a positive integer n (n <= 5000); the next line contains a permutation of the n integers from 0 to n-1.
Output
For each case, output the minimum inversion number on a single line.
Sample Input
10 1 3 6 9 0 8 5 7 4 2
Sample Output
16
Author
CHEN, Gaoli
Source
Recommend
Ignatius.L | We have carefully selected several similar problems for you: 1540 1542 1255 1828 1541
题意:
求最小逆序数。进行n次,每次把第一个数放到最后面。
题解:
求解一组逆序数,之后ans=ans+n-a[i]*2+1;求最小值。
关于逆序数思路(大神神讲解):http://www.cnblogs.com/shenshuyang/archive/2012/07/14/2591859.html
. 离散之后,怎么使用离散后的结果数组来进行树状数组操作,计算出逆序数?
如果数据不是很大, 可以一个个插入到树状数组中,
每插入一个数, 统计比他小的数的个数,
对应的逆序为 i- getsum( aa[i] ),
其中 i 为当前已经插入的数的个数,
getsum( aa[i] )为比 aa[i] 小的数的个数,
i- sum( aa[i] ) 即比 aa[i] 大的个数, 即逆序的个数
但如果数据比较大,就必须采用离散化方法
假设输入的数组是9 1 0 5 4, 离散后的结果aa[] = {5,2,1,4,3};
在离散结果中间结果的基础上,那么其计算逆序数的过程是这么一个过程。
1,输入5, 调用upDate(5, 1),把第5位设置为1
1 2 3 4 5
0 0 0 0 1
计算1-5上比5小的数字存在么? 这里用到了树状数组的getSum(5) = 1操作,
现在用输入的下标1 - getSum(5) = 0 就可以得到对于5的逆序数为0。
2. 输入2, 调用upDate(2, 1),把第2位设置为1
1 2 3 4 5
0 1 0 0 1
计算1-2上比2小的数字存在么? 这里用到了树状数组的getSum(2) = 1操作,
现在用输入的下标2 - getSum(2) = 1 就可以得到对于2的逆序数为1。
3. 输入1, 调用upDate(1, 1),把第1位设置为1
1 2 3 4 5
1 1 0 0 1
计算1-1上比1小的数字存在么? 这里用到了树状数组的getSum(1) = 1操作,
现在用输入的下标 3 - getSum(1) = 2 就可以得到对于1的逆序数为2。
4. 输入4, 调用upDate(4, 1),把第5位设置为1
1 2 3 4 5
1 1 0 1 1
计算1-4上比4小的数字存在么? 这里用到了树状数组的getSum(4) = 3操作,
现在用输入的下标4 - getSum(4) = 1 就可以得到对于4的逆序数为1。
5. 输入3, 调用upDate(3, 1),把第3位设置为1
1 2 3 4 5
1 1 1 1 1
计算1-3上比3小的数字存在么? 这里用到了树状数组的getSum(3) = 3操作,
现在用输入的下标5 - getSum(3) = 2 就可以得到对于3的逆序数为2。
6. 0+1+2+1+2 = 6 这就是最后的逆序数
分析一下时间复杂度,首先用到快速排序,时间复杂度为O(NlogN),
CODE:
线段树:
#include<iostream> #include<cstdio> #include<cstring> #include<cmath> #include<string> #include<algorithm> #include<cstdlib> #include<set> #include<queue> #include<stack> #include<vector> #include<map> #define N 5010 #define Mod 10000007 #define lson l,mid,idx<<1 #define rson mid+1,r,idx<<1|1 #define lc idx<<1 #define rc idx<<1|1 const double EPS = 1e-11; const double PI = acos ( -1.0 ); const double E = 2.718281828; typedef long long ll; const int INF = 1000010; using namespace std; int tree[N<<2],n,a[N]; void build(int l,int r,int idx) { tree[idx]=0; if(l==r) return; int mid=(l+r)>>1; build(lson); build(rson); } void update(int l,int r,int idx,int x) { if(l==r) { tree[idx]++; return; } int mid=(l+r)>>1; if(x<=mid) update(lson,x); else update(rson,x); tree[idx]=tree[rc]+tree[lc]; } int query(int l,int r,int idx,int x,int y) { if(l>=x&&y>=r) { return tree[idx]; } int ans=0; int mid=(l+r)>>1; if(x<=mid) ans+=query(lson,x,y); if(y>mid) ans+=query(rson,x,y); return ans; } int main() { while(cin>>n) { build(1,n,1); for(int i=1; i<=n; i++) { scanf("%d",&a[i]); a[i]++; } int ans=0; for(int i=1; i<=n; i++) { ans+=query(1,n,1,a[i]+1,n); update(1,n,1,a[i]); } int Min=ans; for(int i=1;i<=n;i++) { ans+=n-2*a[i]+1; Min=min(Min,ans); } cout<<Min<<endl; } return 0; }
树状数组:
#include<iostream> #include<cstdio> #include<cstring> #include<cmath> #include<string> #include<algorithm> #include<cstdlib> #include<set> #include<queue> #include<stack> #include<vector> #include<map> #define N 100010 #define Mod 10000007 #define lson l,mid,idx<<1 #define rson mid+1,r,idx<<1|1 #define lc idx<<1 #define rc idx<<1|1 const double EPS = 1e-11; const double PI = acos ( -1.0 ); const double E = 2.718281828; typedef long long ll; const int INF = 1000010; using namespace std; int sum[5005]; int bit[5050],a[5050]; int n; int getsum(int i) { int s=0; while(i>0) { s+=bit[i]; i-=i&-i; } return s; } void add(int i,int x)//更新 { while(i<=n) { bit[i]+=x; i+=i&-i; } } int main() { while(cin>>n) { memset(bit,0,sizeof bit); for(int i=1; i<=n; i++) { scanf("%d",&a[i]); a[i]++; } int ans=0; for(int i=1; i<=n; i++) { add(a[i],1); sum[i]=i-getsum(a[i]); ans+=sum[i]; } int x=0; int Min=ans; for(int i=1; i<=n; i++) { ans+=n-a[i]*2+1; Min=min(Min,ans); } cout<<Min<<endl; } return 0; }