hdu1394 Minimum Inversion Number(求逆序对)
Minimum Inversion Number
Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others)
Total Submission(s): 3466 Accepted Submission(s): 2113
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.
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
题意:共有n-1个序列求最小的逆序对;
方法一:归并 O(n^2);

1 #include<iostream> 2 #define N 5010 3 using namespace std; 4 5 int A[N],B[N],T[N],ans; 6 7 void merge(int s1,int e1,int s2,int e2) 8 { 9 int k,i,j; 10 k=i=s1;j=s2; 11 while(i<=e1&&j<=e2) 12 { 13 if(A[i]<=A[j]) 14 { 15 T[k]=A[i]; 16 k++;i++; 17 } 18 else 19 { 20 T[k]=A[j]; 21 ans+=(e1-i+1);//i后面的肯定都大于A[j] 22 k++;j++; 23 } 24 } 25 while(i<=e1) 26 { 27 T[k++]=A[i++]; 28 } 29 while(j<=e2) 30 { 31 T[k++]=A[j++]; 32 } 33 for(i=s1;i<=e2;i++) A[i]=T[i]; 34 } 35 36 void mergesort(int s,int e) 37 { 38 if(s<e) 39 { 40 int m=(s+e)>>1; 41 mergesort(s,m); 42 mergesort(m+1,e); 43 merge(s,m,m+1,e); 44 } 45 } 46 47 int main() 48 { 49 int n,i,j,t,t1,t2,tt,ttt; 50 while(scanf("%d",&n)!=EOF) 51 { 52 ans=0; 53 for(i=0;i<n;i++) 54 { 55 scanf("%d",&A[i]); 56 B[i]=A[i]; 57 } 58 mergesort(0,n-1); 59 tt=ans; 60 for(i=0;i<n-1;i++) 61 { 62 t1=t2=0; 63 t=B[0]; 64 for(j=1;j<n;j++) 65 { 66 B[j-1]=B[j]; 67 if(t<B[j])t1++; 68 else if(t>B[j]) t2++; 69 } 70 tt=tt+t1-t2; 71 if(ans>tt) ans=tt; 72 B[n-1]=t; 73 } 74 printf("%d\n",ans); 75 } 76 return 0; 77 }
方法二:线段树实现(这也是做这题的原因)(模板)
1、离散化:把起初的元素用 0..n-1 这些数来替代,如2 5 2 6 离散化就是0 0 1 2,不过这个题不要离散化,而已每个元素都是不一样的 = =!!
这样的话你就很快知道比s[i]大的数有多少,比s[i]小的数有多少。
2、每段树中区间 [tree[r].left,tree[r].right] 中有多少个以前出现过。
3、每次输入一个key ,就查找( key,n-1 ]中有多少个数以前出现过,再把 key 插入树中。
4、假设t是当前逆序对的个数,把s[0]移到最后产生的新序列的逆序对为 t+(n-1-s[0])-s[0];循环n-1次就能找到最小值了。

1 #include<iostream> 2 #define N 5010 3 using namespace std; 4 5 struct segtree 6 { 7 int left,right,sum; 8 }tree[N<<2]; 9 int s[N]; 10 11 void build(int r,int a,int b) 12 { 13 tree[r].left=a; 14 tree[r].right=b; 15 tree[r].sum=0; 16 if(a<b) 17 { 18 int m=(a+b)>>1; 19 build(r<<1,a,m); 20 build(r<<1|1,m+1,b); 21 } 22 } 23 24 /* 25 没考虑和a一样大的,也不存在 26 在插入a之前有多少(a,b]之间的数已经插入了,都是逆序对 27 */ 28 int query(int r,int a,int b) 29 { 30 if(tree[r].left==a&&tree[r].right==b) 31 return tree[r].sum; 32 int m=(tree[r].left+tree[r].right)>>1; 33 if(b<=m) 34 return query(r<<1,a,b); 35 else if(a>m) 36 return query(r<<1|1,a,b); 37 else 38 return query(r<<1,a,m)+query(r<<1|1,m+1,b); 39 } 40 41 void update(int r,int key) 42 { 43 tree[r].sum++; 44 if(tree[r].left==tree[r].right) return ; 45 int m; 46 m=(tree[r].left+tree[r].right)>>1; 47 if(key<=m) 48 update(r<<1,key); 49 else 50 update(r<<1|1,key); 51 } 52 53 int main() 54 { 55 int n,i,t,ans; 56 while(cin>>n) 57 { 58 ans=0; 59 build(1,0,n-1); 60 for(i=0;i<n;i++) 61 { 62 cin>>s[i]; 63 ans+=query(1,s[i],n-1); 64 update(1,s[i]); 65 } 66 t=ans; 67 for(i=0;i<n-1;i++) 68 { 69 t=t+(n-1-s[i])-s[i]; 70 if(ans>t) 71 ans=t; 72 } 73 cout<<ans<<endl; 74 } 75 return 0; 76 }