HDU 1394 Minimum Inversion Number(求最小逆序数)

原题链接:传送门

题意:有n个数,依次将第一个数移到最后一个,求这些序列中最小的逆序数。

可以先求出原序列的逆序数,然后依次将第一个数移到最后,这时的逆序数就会减少a[i]个,同时增加n-1-a[i]个。依次求出每一个序列的逆序数,就可以求出这些序列中最小的逆序数了。

这道题可以用线段树来做、也可以用树状数组来做,但既然是求逆序数,那么也可以用归并排序。。


线段树版:

#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;
}



扫描二维码关注公众号,回复: 2341030 查看本文章

归并排序版:

#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;
}



猜你喜欢

转载自blog.csdn.net/qq_16554583/article/details/81069315