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
解题思路
这题折腾了一晚上才AC 还是太粗心了。
因为数据小,貌似暴力也是可以过的。用线段树当然更好。
解题分为两步
①先不考虑移动,用O(nlogn)把初始的逆序对个数算出来(假设是sum)
②用数学公式用O(1)递推sum 找到最小情况的sum
先看第一步,用线段树。每读入一个数我们只考虑在它之前已经读入的数。便于统计我们将所有的s[i]++成为1-n的正整数。再看当前读入的这个数和n之间的这一段距离已经存在了几个数。他们就是当前这个数的”前瞻逆序对”
第二步。假设当前逆序对是sum个。现在考虑第一个数 假设是k 将它放到队伍的最后面。我们知道在第2到第n个数中有k-1个是小于k的,n-k个是大于k的。所以这么一移动,自然逆序对数量就少了k-1个,多了n-k个。这样不断循环,不断更新最小值。
代码
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxn = 5010;
int n;
int s[maxn];
int ans[maxn];
int segTree[maxn<<2];
void update(int node,int k,int l,int r)
{
if(l <= k && k <= r) segTree[node] ++;
if(l == r) return;
if(r < k || l > k) return;
update(node<<1,k,l,(l+r)/2);
update((node<<1)+1,k,(l+r)/2+1,r);
}
int query(int a,int b,int node,int l,int r)
{
if(l > b || r < a) return 0;
if(a <= l && r <= b) return segTree[node];
return query(a,b,node<<1,l,(l+r)/2)+query(a,b,(node<<1)+1,(l+r)/2+1,r);
}
int main()
{
while(scanf("%d",&n) != EOF) {
memset(s,0,sizeof(s));
memset(ans,0,sizeof(ans));
memset(segTree,0,sizeof(segTree));
for(int i = 1 ; i <= n ; i ++) {
scanf("%d",&s[i]);
s[i] ++;
ans[i] = query(s[i],n,1,1,n);
update(1,s[i],1,n);
}
int sum = 0;
for(int i = 1 ; i <= n ; i ++) sum += ans[i];
int mi = sum;
for(int i = 1 ; i < n ; i ++) {
sum = sum+1+n-2*s[i];
if(sum < mi) mi = sum;
}
printf("%d\n",mi);
}
return 0;
}