原题链接:传送门
**题意:**有n个数,依次将第一个数移到最后一个,求这些序列中最小的逆序数。
可以先求出原序列的逆序数,然后依次将第一个数移到最后,这时的逆序数就会减少a[i]
个,同时增加n-1-a[i]
个。依次求出每一个序列的逆序数,就可以求出这些序列中最小的逆序数了。
这道题可以用线段树来做、也可以用树状数组来做,但既然是求逆序数,那么也可以用归并排序。。
归并排序版:
#include <iostream>
#include <cstring>
#include <cstdio>
using namespace std;
const int N = 5005;
int A[N],B[N];
int ans,sum;
int n;
void Merge(int A[],int l,int m,int r) {
int i = l;
int j = m+1;
int cnt = l;
while(i <= m && j <= r) {
if(A[i] <= A[j]) {
B[cnt++] = A[i++];
} else {
B[cnt++] = A[j++];
sum += j - cnt;
}
}
while(i <= m) {
B[cnt++] = A[i++];
}
while(j <= r) {
B[cnt++] = A[j++];
}
for(int k=l; k<=r; k++) {
A[k] = B[k];
}
}
void MergeSort(int A[],int l,int r) {
if(l == r) {
return ;
}
int m = (r-l)/2 + l;
MergeSort(A,l,m);
MergeSort(A,m+1,r);
Merge(A,l,m,r);
}
int main() {
while(~scanf("%d",&n)) {
int temp[n];
for(int i=0; i<n; i++) {
scanf("%d",&A[i]);
temp[i] = A[i]; //还要用个辅助数组来记录原数组,因为排序后原数组就变了。
}
sum = 0;
MergeSort(A,0,n-1);
ans = sum;
for(int i=0; i<n; i++) {
sum = sum - temp[i] + (n-1-temp[i]);
ans = min(ans,sum);
}
printf("%d\n",ans);
}
return 0;
}
线段树版:
#include <iostream>
#include <cstring>
#include <cstdio>
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
using namespace std;
const int N = 5005;
int num[N << 2];
int n,a[N];
void Updata(int pos,int l,int r,int rt){
num[rt] ++;
int m = (r-l)/2 + l;
if(l == r) return ;
if(pos <= m) Updata(pos,lson);
else Updata(pos,rson);
}
int Query(int pos,int l,int r,int rt){
int ans = 0;
int m = (r-l)/2 + l;
if(pos == r) return num[rt];
if(pos <= m) ans += Query(pos,lson);
else ans += num[rt<<1] + Query(pos,rson); //要加上左边节点的值
return ans;
}
int main(){
int ans,sum;
while(~scanf("%d",&n)){
memset(num,0,sizeof(num)); //空树,直接memset就可以
sum = 0;
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
a[i] ++;
Updata(a[i],1,n,1);
sum += (i-Query(a[i],1,n,1)); //i表示前面已经存i个数,
} //减去a[i]及a[i]的数,就是当前a[i]的逆序数
ans = sum;
for(int i=1;i<=n;i++){
sum = sum - (a[i]-1) + (n-a[i]); //把a[i]移到最后 逆序数减少a[i],增加n-1-a[i].
ans = min(ans,sum);
}
printf("%d\n",ans);
}
return 0;
}
树状数组版:
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#define lowbit(x) x&(-x)
using namespace std;
const int N = 5005;
int tree[N],a[N];
int n;
void Add(int k,int d) {
while(k <= n) {
tree[k] += d;
k += lowbit(k);
}
}
int Query(int k) {
int ret = 0;
while(k) {
ret += tree[k];
k -= lowbit(k);
}
return ret;
}
int main() {
int x;
int sum,ans;
while(~scanf("%d",&n)) {
fill(tree,tree+N,0); //另一种初始化数组tree的方式
sum = 0;
for(int i=1; i<=n; i++) {
scanf("%d",&a[i]);
Add(a[i]+1,1);
sum += (a[i]+1 - Query(a[i]+1)); //**求a[i]之前有几个大于a[i]的数,即逆序数**
}
ans = sum;
for(int i=1; i<=n; i++) {
sum = sum - a[i] + (n - 1 - a[i]);
ans = min(ans,sum);
}
printf("%d\n",ans);
}
return 0;
}