Minimum Inversion Number
Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others)
Total Submission(s): 17737 Accepted Submission(s): 10763
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.
题目链接:HDU 1394
主要操作:单点更新,区间求和
通过此题学习到了如何用NlogN的时间来求一个数列的逆序对个数(简称逆序数),先上暴力求法
暴力求法代码:
int cnt=0; for (int i=1; i<=n; ++i) { for (int j=i+1; j<=n; ++i) { if(arr[i]>arr[j])//i<j但arr[i]却大于arr[j],这就是一个逆序对<arr[i],arr[j]> ++cnt; } } printf("%d ",cnt);//cnt即为逆序对个数
那显然题目中的n可达5000,一平方估计得TLE,然后学习了一下用树状数组或线段树来求的方法。
主要思路:按照顺序读入一个数,记为arr[i]吧,然后对【arr[i]+1, end】求和记为sum,为什么这样就可以得到arr[i]的逆序对呢?
想想逆序对的定义:i<j且arr[i]>arr[j](假设i<j前面),显然题目按顺序读入已经无形中满足了i<j的要求,也就是说正常上升序列应该是i<j且arr[i]<arr[j] ,但是你出现了一个arr[i]>arr[j],也就是说让【arr[j],end】之间的arr[i]这个点的值发生变化(变为1),那我一旦对【arr[j]+1,end】求和就肯定会出现sum>0的情况,那这个大于0是谁造成的呢?就是某个大于arr[j]的arr[i],不用管他是谁,反正只要arr[i]>arr[j]就能对【arr[j],end】一段求和造成影响,那显然一个arr[i]与arr[j]能组成一对,那n个就组成n对,那每一个的影响值都设为1,那求和求出来的就是对数。
然后得到了最初的逆序数怎么往后推?那就考虑开头的数移到末尾的影响,显然比开头的arr[0]小的数有arr[0]-1个记为low(题目中范围为0~n-1的自然数,且会全部出现且不重复),然后除去开头的数一共有n-1个,那么剩下比arr[0]大的数有(n-1)-(arr[0]-1)个即n-arr[0]个记为up。然后看开头的移到末尾,原本的逆序数变成了正序,原本的正序变成了逆序,因此移动之后的sum'=sum+(正序变为逆序就要加)up-(逆序变为正序就要减)low。
代入n与arr[0]得到sum'=sum+n-arr[0]-(arr[0]-1)=sum+n-arr[0]-arr[0]+1。
最后值得注意的是用树状数组的话题目中范围是0~n-1,出现0会死循环,因此arr[i]要自增1再操作。
树状数组代码(31ms):
#include<iostream> #include<algorithm> #include<cstdlib> #include<sstream> #include<cstring> #include<cstdio> #include<string> #include<deque> #include<stack> #include<cmath> #include<queue> #include<set> #include<map> using namespace std; #define INF 0x3f3f3f3f #define CLR(x,y) memset(x,y,sizeof(x)) #define LC(x) (x<<1) #define RC(x) ((x<<1)+1) #define MID(x,y) ((x+y)>>1) typedef pair<int,int> pii; typedef long long LL; const double PI=acos(-1.0); const int N=5010; int T[N]; inline int lowbit(const int &n) { return (n&-n); } void add(int k,int val) { while (k<N) { T[k]+=val; k+=lowbit(k); } } int getsum(int k) { int r=0; while (k) { r+=T[k]; k-=lowbit(k); } return r; } int arr[N]; int main(void) { int n,i,j; while (~scanf("%d",&n)) { CLR(arr,0); CLR(T,0); int sum=0; for (i=1; i<=n; ++i) { scanf("%d",&arr[i]); ++arr[i]; sum+=(getsum(n)-getsum(arr[i])); add(arr[i],1); } int minm=INF; for (i=1; i<=n; ++i) { sum=sum+(n-arr[i])-arr[i]-1); if(sum<minm) minm=sum; } printf("%d ",minm); } return 0; }
线段树代码 (62ms):
#include<iostream> #include<algorithm> #include<cstdlib> #include<sstream> #include<cstring> #include<cstdio> #include<string> #include<deque> #include<stack> #include<cmath> #include<queue> #include<set> #include<map> using namespace std; #define INF 0x3f3f3f3f #define CLR(x,y) memset(x,y,sizeof(x)) #define LC(x) (x<<1) #define RC(x) ((x<<1)+1) #define MID(x,y) ((x+y)>>1) typedef pair<int,int> pii; typedef long long LL; const double PI=acos(-1.0); const int N=5010; struct info { int l,r,mid; int sum; }T[N<<2]; void pushup(int k) { T[k].sum=T[LC(k)].sum+T[RC(k)].sum; }; void build(int k,int l,int r) { T[k].sum=0; T[k].l=l; T[k].r=r; T[k].mid=MID(l,r); if(l==r) return ; build(LC(k),l,T[k].mid); build(RC(k),T[k].mid+1,r); //pushup(k); } void update(int k,int x,int val) { if(T[k].l==T[k].r&&T[k].l==x) T[k].sum+=val; else { if(x<=T[k].mid) update(LC(k),x,val); else update(RC(k),x,val); pushup(k); } } int range_query(int k,int l,int r) { if(l<=T[k].l&&r>=T[k].r) return T[k].sum; else { if(r<=T[k].mid) return range_query(LC(k),l,r); else if(l>T[k].mid) return range_query(RC(k),l,r); else return range_query(LC(k),l,T[k].mid)+range_query(RC(k),T[k].mid+1,r); } } int arr[N]; int Scan() { int res=0,ch,flag=0; if((ch=getchar())=='-') flag=1; else if(ch>='0'&&ch<='9') res=ch-'0'; while((ch=getchar())>='0'&&ch<='9') res=res*10+ch-'0'; return flag?-res:res; } int main(void) { int n,i,j; while (~scanf("%d",&n)) { int sum=0,minm=INF; build(1,0,N); for (i=0; i<n; ++i) { arr[i]=Scan(); sum+=range_query(1,arr[i]+1,N); update(1,arr[i],1); } for (i=0; i<n; ++i) { sum=sum+((n-1)-arr[i])-arr[i]; if(sum<minm) minm=sum; } printf("%d ",minm); } return 0; }