HDU1394 Minimum Inversion Number(线段树单点更新,暴力,逆序数)
2017-04-30 20:50
429 查看
题目:
Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others)
Total Submission(s): 19790 Accepted Submission(s): 11891
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
ZOJ Monthly, January 2003
Recommend
Ignatius.L | We have carefully selected several similar problems for you: 1698 1540 1542 1255 1828
Statistic | Submit | Discuss | Note
思路:
说实话。。这题的题意不好懂,所幸看到了大牛的博客,详细讲了题意,我就顺便把题意copy过来吧。。
弄了半天才弄懂题目的意思,就是求最小的逆序数,在此贴下逆序数的概念
在一个排列中,如果一对数的前后位置与大小顺序相反,即前面的数大于后面的数,那么它们就称为一个逆序。一个排列中逆序的总数就称为这个排列的逆序数。逆序数为偶数的排列称为偶排列;逆序数为奇数的排列称为奇排列。如2431中,21,43,41,31是逆序,逆序数是4,为偶排列。
也是就说,对于n个不同的元素,先规定各元素之间有一个标准次序(例如n个 不同的自然数,可规定从小到大为标准次序),于是在这n个元素的任一排列中,当某两个元素的先后次序与标准次序不同时,就说有1个逆序。一个排列中所有逆序总数叫做这个排列的逆序数。
题目的意思就好比给出一个序列
如:0 3 4 1 2
设逆序数初始n = 0;
由于0后面没有比它小的,n = 0
3后面有1,2 n = 2
4后面有1,2,n = 2+2 = 4;
所以该序列逆序数为 4
其根据题意移动产生的序列有
3 4 1 2 0 逆序数:8
4 1 2 0 3 逆序数:6
1 2 0 3 4 逆序数:2
2 0 3 4 1 逆序数:4
所以最小逆序数为2
在知道了题意以后,我们采用两种方法来进行这个题的求解。。。
①暴力法:直接第一遍for循环遍历一遍比他大的数,然后把剩下的数给递推出来,这个题有一个结论,如果求出第一种情况的逆序列,其他的可以通过递推来搞出来,一开始是t[1],t[2],t[3]....t
,它的逆序列个数是N个,如果把t[1]放到t
后面,逆序列个数会减少t[1]个,相应会增加N-(t[1]+1)个 .利用这个结论在把其他的情况递推出来,求最小值
②线段树:建立一棵空树,然后每一次查询询问的是当前有几个比当前这个数大的数,然后每次把这个点插入这一课线段树,最后就求出了第一个的逆序数,然后在用这个结论依次求出其他的,然后取最小值。
代码1(线段树):
Minimum Inversion Number
Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others)Total Submission(s): 19790 Accepted Submission(s): 11891
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
ZOJ Monthly, January 2003
Recommend
Ignatius.L | We have carefully selected several similar problems for you: 1698 1540 1542 1255 1828
Statistic | Submit | Discuss | Note
思路:
说实话。。这题的题意不好懂,所幸看到了大牛的博客,详细讲了题意,我就顺便把题意copy过来吧。。
弄了半天才弄懂题目的意思,就是求最小的逆序数,在此贴下逆序数的概念
在一个排列中,如果一对数的前后位置与大小顺序相反,即前面的数大于后面的数,那么它们就称为一个逆序。一个排列中逆序的总数就称为这个排列的逆序数。逆序数为偶数的排列称为偶排列;逆序数为奇数的排列称为奇排列。如2431中,21,43,41,31是逆序,逆序数是4,为偶排列。
也是就说,对于n个不同的元素,先规定各元素之间有一个标准次序(例如n个 不同的自然数,可规定从小到大为标准次序),于是在这n个元素的任一排列中,当某两个元素的先后次序与标准次序不同时,就说有1个逆序。一个排列中所有逆序总数叫做这个排列的逆序数。
题目的意思就好比给出一个序列
如:0 3 4 1 2
设逆序数初始n = 0;
由于0后面没有比它小的,n = 0
3后面有1,2 n = 2
4后面有1,2,n = 2+2 = 4;
所以该序列逆序数为 4
其根据题意移动产生的序列有
3 4 1 2 0 逆序数:8
4 1 2 0 3 逆序数:6
1 2 0 3 4 逆序数:2
2 0 3 4 1 逆序数:4
所以最小逆序数为2
在知道了题意以后,我们采用两种方法来进行这个题的求解。。。
①暴力法:直接第一遍for循环遍历一遍比他大的数,然后把剩下的数给递推出来,这个题有一个结论,如果求出第一种情况的逆序列,其他的可以通过递推来搞出来,一开始是t[1],t[2],t[3]....t
,它的逆序列个数是N个,如果把t[1]放到t
后面,逆序列个数会减少t[1]个,相应会增加N-(t[1]+1)个 .利用这个结论在把其他的情况递推出来,求最小值
②线段树:建立一棵空树,然后每一次查询询问的是当前有几个比当前这个数大的数,然后每次把这个点插入这一课线段树,最后就求出了第一个的逆序数,然后在用这个结论依次求出其他的,然后取最小值。
代码1(线段树):
#include <cstdio> #include <cstring> #include <cctype> #include <string> #include <set> #include <iostream> #include <stack> #include <cmath> #include <queue> #include <vector> #include <algorithm> #define mem(a,b) memset(a,b,sizeof(a)) #define inf 0x3f3f3f3f #define mod 10000007 #define debug() puts("what the fuck!!!") #define N 1000020 #define M 1000000 #define ll long long using namespace std; #define lson l,m,rt<<1 #define rson m+1,r,rt<<1|1 //位运算,|可以理解为+ int sum[4*N]; void pushup(int rt)//更新该节点维护的值,求和 { sum[rt]=sum[rt<<1]+sum[rt<<1|1]; } void build(int l,int r,int rt)//建立线段树 { sum[rt]=0; if(l==r) return; int m=(l+r)>>1; build(lson); build(rson); } void update(int p,int l,int r,int rt)//单点更新 { if(l==r) { sum[rt]++; return; } int m=(l+r)>>1; if(p<=m) update(p,lson); else update(p,rson); pushup(rt); } int query(int L,int R,int l,int r,int rt)//要查询的区间和当前的左右节点和根节点 { if(L<=l&&r<=R)//[l,r]∈[L,R] { return sum[rt]; } int m=(l+r)>>1;//找中间点 int ret=0; if(L<=m) ret+=query(L,R,lson); if(R>m) ret+=query(L,R,rson); return ret; } int x ; int main() { int n; while(~scanf("%d",&n)) { build(0,n-1,1);//建立一棵空树 int cnt=0; for(int i=0;i<n;i++) { scanf("%d",&x[i]); cnt+=query(x[i],n-1,0,n-1,1);//返回比x[i]大的个数 update(x[i],0,n-1,1);//插入x[i] } int ret=cnt; for(int i=0;i<n;i++) { cnt+=n-x[i]-x[i]-1;//递推式 ret=min(ret,cnt); } printf("%d\n",ret); } return 0; }代码2(暴力):
#include <cstdio> #include <cstring> #include <cctype> #include <string> #include <set> #include <iostream> #include <stack> #include <cmath> #include <queue> #include <vector> #include <algorithm> #define mem(a,b) memset(a,b,sizeof(a)) #define inf 0x3f3f3f3f #define mod 10000007 #define debug() puts("what the fuck!!!") #define N 1000020 #define M 1000000 #define ll long long using namespace std; int a[6000]; int main() { int n; while(~scanf("%d",&n)) { int ans=inf; for(int i=0; i<n; i++) scanf("%d",&a[i]); int cnt=0; for(int i=0; i<n; i++) for(int j=i+1; j<n; j++) { if(a[i]>a[j]) cnt++; } if(ans>cnt) ans=cnt; for(int i=0; i<n; i++) { cnt=cnt-a[i]+n-1-a[i]; if(ans>cnt) ans=cnt; } printf("%d\n",ans); } return 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 (线段树 单点更新 求逆序数)
- poj1394——Minimum Inversion Number——————【线段树单点更新、逆序数特性】
- HDU 1394 Minimum Inversion Number(单点更新 + 求逆序数)
- HDU1394 Minimum Inversion Number(线段树单点更新)
- hdu1394 Minimum Inversion Number 线段树,单点更新
- hdu 1394 Minimum Inversion Number(最小逆序数) 线段树/暴力
- Minimum Inversion Number----HDU_1394----线段树之单点更新
- HDU 1394 Minimum Inversion Number (线段树,单点更新)
- HDU 1394 Minimum Inversion Number(线段树:单点更新,区间求和)
- HDU1394-Minimum Inversion Number(线段树单点更新)
- ZOJ 1484 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(线段树 单点更新)