HDU 1394 Minimum Inversion Number (线段树&&暴力)
Minimum Inversion Number
Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others)
Total Submission(s): 10477 Accepted Submission(s): 6464
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
Author
CHEN, Gaoli
Source
Recommend
Ignatius.L
题目的意思就是说给你一个序列,求这个序列的逆序数的对数,然后将第一个元素放到最后,从而生成了一个新的序列,直到最后一个元素到第一个时停止,求生成的序列中,最少的逆序数的对数是多少。
我谈谈我的做法
1.暴力,每次找出新序列的逆序数的对数,然后看看是不是最小值,最后输出最小值即可
但这里要点技巧,在寻找新的序列的逆序数的对数时,没有必要去扫一遍出结果。
可以想想,因为序列的数字是从0~n-1连续的,那么当第一个数移到最后去的时候,原来比这个数大的数字变成了逆序数,比这个数小的数就不是逆序数了
举个例子:
3 2 4 5
本来 3 2是逆序数,3 4,3 5不是逆序数,当序列变成2 4 5 3时,原来的3 4变成了4 3是逆序数,2 3就不是逆序数了
所以在原有的序列中比第一个元素要小的数个数为(即逆序数的对数) low[a[i]]=a[i],所以可以推出比第一个元素要大的数的个数为up[a[i]]=n-1-a[i]
那么新的序列中逆序数的对数是 sum=sum-low[a[i]]+up[a[i]]=sum-a[i]+(n-1-a[i])
暴力的代码
1 #include<cstdio> 2 #include<cstring> 3 #include<stdlib.h> 4 #include<algorithm> 5 using namespace std; 6 int a[5005]; 7 int main() 8 { 9 int n,i,j; 10 while(scanf("%d",&n)!=EOF) 11 { 12 int sum=0; 13 for(i=0;i<n;i++) 14 { 15 scanf("%d",&a[i]); 16 for(j=0;j<i;j++) 17 if(a[j]>a[i]) 18 sum++; 19 } 20 int ans=sum; 21 for(i=0;i<n;i++) 22 { 23 sum=sum-a[i]+(n-a[i]-1); 24 if(sum<ans) 25 ans=sum; 26 } 27 printf("%d\n",ans); 28 } 29 return 0; 30 }
2.线段树
同样的一开始也是要找出逆序数的对数,暴力方法是从第一个数到现在这个数扫一遍查找,线段树的方法就是用线段树去查询,会更快
后面对新序列的逆序数对数的处理时相同的
线段树的代码
1 #include<cstdio> 2 #include<cstring> 3 #include<stdlib.h> 4 #include<algorithm> 5 using namespace std; 6 struct node 7 { 8 int l,r; 9 int num; 10 int mid() 11 { 12 return (l+r)>>1; 13 } 14 }a[5000*4]; 15 int b[5005]; 16 void btree(int l,int r,int step) 17 { 18 a[step].l=l; 19 a[step].r=r; 20 a[step].num=0; 21 if(l==r) return ; 22 int mid=a[step].mid(); 23 btree(l,mid,step*2); 24 btree(mid+1,r,step*2+1); 25 } 26 27 void ptree(int step,int vis) 28 { 29 a[step].num++; 30 if(a[step].l==a[step].r) return ; 31 int mid=a[step].mid(); 32 if(vis>mid) 33 ptree(step*2+1,vis); 34 else 35 ptree(step*2,vis); 36 } 37 38 int fintree(int step,int x,int y) 39 { 40 if(a[step].l==x&&a[step].r==y) 41 return a[step].num; 42 int mid=a[step].mid(); 43 if(x>mid) 44 return fintree(step*2+1,x,y); 45 else if(y<=mid) 46 return fintree(step*2,x,y); 47 else 48 return fintree(step*2,x,mid)+fintree(step*2+1,mid+1,y); 49 } 50 int main() 51 { 52 int n,i,j; 53 while(scanf("%d",&n)!=EOF) 54 { 55 int ans=0; 56 btree(0,n-1,1); 57 for(i=0;i<n;i++) 58 { 59 scanf("%d",&b[i]); 60 ans+=fintree(1,b[i],n-1); 61 ptree(1,b[i]); 62 } 63 int minn=ans; 64 for(i=0;i<n;i++) 65 { 66 ans=ans-b[i]+(n-1-b[i]); 67 if(minn>ans) minn=ans; 68 } 69 printf("%d\n",minn); 70 } 71 return 0; 72 }