HDU1394 Minimum Inversion Number(线段树OR归并排序)
Minimum Inversion Number
Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others)
Total Submission(s): 15113 Accepted Submission(s): 9230
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
题意:求不同数列中最小的逆序对数。
思路:
统计a[i]前面的,且比它大的数
这样做的话,就可以利用输入的时效性,每输入一个数,就把这个数的num[i]值为1,
然后统计比这个数大的数的num和,
因为这里的和一定是在这个数列中比a[i]大,且在它前面出现的数之和,
然后把把这个和加到总逆序数sum里。
这样做的话直接的暴力作法依然是n2,但是,
我们可以在,统计比这个数大的数的num和这一步进行优化,利用线段树求区间域值的复杂度是logn,
所以总体复杂度就降到了nlogn。
再来看这道题,求得初始数列的逆序数后,再求其他排列的逆序数有一个规律,就是
sum = sum + (n - 1 - a[i]) - a[i];比如原来的逆序数是sum,把a[0]移到最后后,减少逆序数a[0],同时增加逆序数n-a[0]-1个。
#include <cstdio> #include <iostream> #include <cstdlib> #include <algorithm> #include <ctime> #include <cmath> #include <string> #include <cstring> #include <stack> #include <queue> #include <list> #include <vector> #include <map> #include <set> using namespace std; const int INF=0x3f3f3f3f; const double eps=1e-10; const double PI=acos(-1.0); #define maxn 5500 int tre[4*maxn]; void update(int num, int le, int ri, int x) { if(le == ri) { tre[num] = 1; return; } int mid = (le+ri)/2; if(x<=mid) update(num*2,le,mid,x); else update(num*2+1,mid+1,ri,x); tre[num] = tre[num*2] + tre[num*2+1]; } int query(int num, int le, int ri, int x, int y) { if(x<=le&&y>=ri) { return tre[num]; } int mid = (le+ri)/2; int ans = 0; if(x<=mid) ans += query(num*2,le,mid,x,y); if(y>mid) ans += query(num*2+1,mid+1,ri,x,y); return ans; } int a[maxn]; int main() { int n; while(~scanf("%d", &n)) { memset(tre, 0, sizeof tre); int sum = 0; for(int i = 0; i < n; i++) { scanf("%d", &a[i]); update(1,0,n-1,a[i]); sum += query(1,0,n-1,a[i]+1,n-1); } int ans = sum; for(int i = 0; i < n; i++) { sum = sum - a[i] + (n-1-a[i]); ans = min(ans, sum); } printf("%d\n", ans); } return 0; }
方法二:归并排序求逆序对。
#include <cstdio> #include <iostream> #include <cstdlib> #include <algorithm> #include <ctime> #include <cmath> #include <string> #include <cstring> #include <stack> #include <queue> #include <list> #include <vector> #include <map> #include <set> using namespace std; const int INF=0x3f3f3f3f; const double eps=1e-10; const double PI=acos(-1.0); #define maxn 5500 int cnt; int t[maxn],a[maxn],b[maxn]; void merge_sort(int x,int y)//归并排序模板 { if(y-x>1) { int m=x+(y-x)/2; int p=x,q=m,i=x; merge_sort(x,m); merge_sort(m,y); while(p<m||q<y) { if(q>=y||(p<m&&a[p]<=a[q])) t[i++]=a[p++]; else { t[i++]=a[q++]; cnt+=m-p; } } for(i=x;i<y;i++) a[i]=t[i]; } } int main() { int n; while(~scanf("%d", &n)) { for(int i = 0; i < n; i++) { scanf("%d", &a[i]); b[i] = a[i]; } cnt = 0; merge_sort(0,n); int ans = cnt; for(int i = 0; i < n; i++) { cnt = cnt - b[i] + (n-1-b[i]); ans = min(ans, cnt); } printf("%d\n", ans); } return 0; }