HDU 1394 Minimum Inversion Number(求逆序对+线段树||归并排序)
来源:互联网 发布:男士大衣品牌 知乎 编辑:程序博客网 时间:2024/05/29 03:20
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
101 3 6 9 0 8 5 7 4 2
Sample Output
16
解题思路:
求逆序对的进阶版,这是线段树专题,我一开始就用归并排序ac了比他们都快hhhhh,不过线段树还是要好好学的,百度搜了一个解法,可惜没备注,理解了我一个下午,加上我自己理解把代码奉上吧,不知道有没有理解对,引用:逆序数求得之后,把第一个数移到最后的逆序数是可以直接得到的,比如原来的逆序数是ans,把a[0]移到最后后,减少逆序数a[0],同时增加逆序数n-a[0]-1个,就是ans-a[0]+n-a[0]-1,然后循环求最小值就行了。
代码:
先是归并排序,花的时间居然比线段树少:
#include<algorithm>#include<iostream>#include<cstring>#include<cstdio>#include<math.h>#include<string>#include<stdio.h>#include<queue>#include<stack>#include<map>using namespace std;int a[5005],b[5005],n,ans;void Merge(int l,int r,int mid){ int t[5005],i=l,j=mid+1,k=l; while(i<=mid&&j<=r) { if(a[i]>a[j]) { t[k]=a[j]; ans+=mid-i+1; k++; j++; } else { t[k]=a[i]; i++; k++; } } while(i<=mid) { t[k]=a[i]; k++; i++; } while(j<=r) { t[k]=a[j]; k++; j++; } for(i=l;i<=r;i++) a[i]=t[i];}void guibin(int l,int r){ if(l>=r) return; int mid=(l+r)/2; guibin(l,mid); guibin(mid+1,r); Merge(l,r,mid);}int main(){ int i,j; while(scanf("%d",&n)!=EOF) { for(i=1;i<=n;i++) { scanf("%d",&a[i]); b[i]=a[i]; } ans=0; guibin(1,n); int minn=ans; for(i=1;i<=n;i++) { ans+=n-b[i]-b[i]-1; if(ans<minn) minn=ans; } printf("%d\n",minn); } return 0;}
然后是线段树代码:
#include<algorithm>#include<iostream>#include<cstring>#include<cstdio>#include<math.h>#include<string>#include<stdio.h>#include<queue>#include<stack>#include<map>using namespace std;struct node{ int l,r; int num;}t[20005];//线段树大小等于数据范围*4int a[5005],n;void Build(int l,int r,int num)//递归建树{ t[num].l=l; t[num].r=r; t[num].num=0; if(l==r) return; int mid=(l+r)/2; Build(l,mid,num*2); Build(mid+1,r,num*2+1);}int Find(int num,int x,int y)//访问函数,查找[x,y]区间内有多少已经存在的数{ int l=t[num].l; int r=t[num].r; if(l==x&&r==y) { return t[num].num; } int mid=(l+r)/2; int s=0; if(x<=mid) s+=Find(num*2,x,min(y,mid));//如果一部分线段在t[num]的左边,访问左区间 if(y>mid) s+=Find(num*2+1,max(mid+1,x),y);//如果一部分线段在t[num]的右边,访问右区间 return s;}void update(int num,int x){ t[num].num++;//把包含x的所有区间的数目加一 int l=t[num].l; int r=t[num].r; if(l==r) { return; } int mid=(l+r)/2; if(x<=mid) update(num*2,x); else update(num*2+1,x);}int main(){ int i,j,ans; while(scanf("%d",&n)!=EOF) { ans=0; Build(1,n,1); for(i=1;i<=n;i++) { scanf("%d",&a[i]); ans+=Find(1,1,a[i]+1);//在[1,a[i]+1]区间内查找之前已经存在了的比a[i]小的数的和,a[i]+1是因为从0开始 update(1,a[i]+1);//添加了一个节点以后线段树的区间更新 } ans=n*(n-1)/2-ans;//所有可能对数减去顺序对数为逆序对数 int minn=ans; for(i=1;i<=n;i++) { ans+=n-a[i]-a[i]-1;//由推出的公式求出后面的逆序对数 if(ans<minn) minn=ans; } printf("%d\n",minn); } return 0;}
逆序数求得之后,把第一个数移到最后的逆序数是可以直接得到的。
比如原来的逆序数是ans,把a[0]移到最后后,减少逆序数a[0],同时增加逆序数n-a[0]-1个
就是ans-a[0]+n-a[0]-1;
只要i从0-n-1循环一遍取最小值就可以了。
阅读全文
0 0
- HDU 1394 Minimum Inversion Number(求逆序对+线段树||归并排序)
- HDU 1394 Minimum Inversion Number(线段树/归并排序求逆序对数)
- [hdu]1394 Minimum Inversion Number -- 暴力求逆序、树状数组求逆序、线段树求逆序、归并排序求逆序
- hdu 1394 Minimum Inversion Number(线段树求逆序对)
- HDU 1394 Minimum Inversion Number (线段树 求逆序对)
- HDU 1394 Minimum Inversion Number (归并排序求逆序对)
- HDU 1394 Minimum Inversion Number(循环数组求逆序数)(暴力,归并排序,线段树)
- hdu 1394 Minimum Inversion Number(线段树+逆序对)
- HDU 1394 Minimum Inversion Number( 归并排序 & 线段树 )
- [HDU 1394]Minimum Inversion Number(归并排序/线段树)
- HDU 1394 Minimum Inversion Number(线段树求最小逆序数对)
- HDU 1394 Minimum Inversion Number(线段树求最小逆序数对)
- HDU 1394 Minimum Inversion Number(线段树求逆序对数目)
- hdu-1394Minimum Inversion Number(暴力解法或者线段树 求最少逆序对)
- hdoj 1394 Minimum Inversion Number【线段树求逆序对】
- hdu 1394 Minimum Inversion Number 逆序数 线段树&归并排序
- HDU 1394 Minimum Inversion Number 线段树求逆序数
- hdu 1394 Minimum Inversion Number 线段树求逆序数
- bzoj2843 极地旅行社
- Java文件流关闭和垃圾回收问题 (文件无法删除问题)
- 一个农村女孩的独白:考大学有什么用?考过的和没考的,都要看看
- String转json简单方法
- C编程(二)基本输入输出函数用法
- HDU 1394 Minimum Inversion Number(求逆序对+线段树||归并排序)
- 在Spring MVC框架下利用RESTful API和MongoDB实现用户留言与邮件反馈
- Linux下time命令
- UITextView的text转化为HTML, HTML转化成UITextView的text
- Android 6.0+: 动态权限管理的解决方案
- linux 多台主机时间同步
- django初体验
- 新浪微博Oauth授权认证登录
- 已知dsc 和fdf ,编译bios fd