HDU 1394 - Minimum Inversion Number(逆序数-线段树 | 树状数组)

- HDU 1394 -

Minimum Inversion Number

Time Limit: 2000/1000 MS (Java/Others) | Memory Limit: 65536/32768 K (Java/Others)

题意:

给定数字序列 a1,a2,…,an 的反转数是满足 i < j 和 ai > aj 的对(ai,aj)的数量。
对于给定的数字序列 a1,a2,…,an,把该序列最前面的数移到序列最后面,就会得到一个新的序列,经过 n-1 次该操作后,我们就有一共 n 个序列,找出这些序列中的最小反转数。
输入:有多组测试数据,每组第一行给定一个整数 n ,接下来一行包含从0到n-1的n个整数的排列。

数据范围:

n <= 5000

解题思路:

线段树 or 树状数组

这题本质上是求序列中的逆序对数,并求出所有序列中逆序对数最少的那个。之前求逆序数都是用的树状数组,这还是第一次用线段树来求。

代码:

① 线段树:
Exe.Time : 46MS | Exe.Memory : 1776K

#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <algorithm>
#include <cmath>
#include <stack>
#include <queue>
#include <vector>
#include <map>
#include <set>
using namespace std;
#define INF 0x3f3f3f3f
#define zero 1e-7
#define lowbit(x) x&(-x)

typedef long long ll;
const int N=1e5+5;
const int maxn=5e3+5;

int a[maxn], sum[maxn<<2];

void build(int l, int r, int p) {//建一棵空树
    sum[p]=0;
    if(l==r) return ;
    int mid=(l+r)>>1;
    build(l, mid, p<<1);
    build(mid+1, r, (p<<1)|1);
}

void update(int l, int r, int p, int c) {
    if(l==r) {
        sum[p]++;
        return ;
    }
    int mid=(l+r)>>1;
    if(c<=mid) update(l, mid, p<<1, c);
    else update(mid+1, r, (p<<1)|1, c);
    sum[p]=sum[p<<1]+sum[(p<<1)|1];
}

int query(int l, int r, int p, int s, int t) {
    if(s<=l && r<=t) return sum[p];
    int mid=(l+r)>>1;
    int ans=0;
    if(s<=mid) ans+=query(l, mid, p<<1, s, t);
    if(t>mid) ans+=query(mid+1, r, (p<<1)|1, s, t);
    return ans;
    //printf("a[i]=%d, ans=%d\n", s, ans);
}

int main() {
    int n;
    while(scanf("%d", &n)!=EOF) {
        build(0, n-1, 1);
        int ans=0;
        for(int i=0; i<n; i++) {
            scanf("%d", &a[i]);
            ans+=query(0, n-1, 1, a[i], n);//区间,节点,查询区间
            update(0, n-1, 1, a[i]);//区间,节点,待更新的叶节点
        }
        int res=ans;
        for(int i=0; i<n; i++) {
            ans+=n-a[i]-a[i]-1;//减去比a[i]小的(a[i]个),加上比a[i]大的(n-a[i]-1个)
            res=min(res, ans);
            //printf("%dans=%3d\n", i+1, ans);
        }
        printf("%d\n", res);
    }
    return 0;
}

② 树状数组:
Exe.Time : 46MS | Exe.Memory : 1764K

#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <algorithm>
#include <cmath>
#include <stack>
#include <queue>
#include <vector>
#include <map>
#include <set>
using namespace std;
#define INF 0x3f3f3f3f
#define zero 1e-7
#define lowbit(x) x&(-x)

typedef long long ll;
const int N=1e5+5;
const int maxn=5e3+5;

int a[maxn], sum[maxn], n;

void add(int x) {
    while(x<=n) {
        sum[x]++;
        x+=lowbit(x);
    }
    return ;
}

int query(int x) {
    int ans=0;
    while(x) {
        ans+=sum[x];
        x-=lowbit(x);
    }
    return ans;
}

int main() {
    while(scanf("%d", &n)!=EOF) {
        memset(sum, 0, sizeof(sum));
        for(int i=1; i<=n; i++) {
            scanf("%d", &a[i]);
            a[i]++;
        }
        int ans=0;
        for(int i=n; i; i--) {
            ans+=query(a[i]);
            add(a[i]);
        }
        int res=ans;
        for(int i=1; i<n; i++) {
            int temp=query(a[i]-1);
            ans-=temp;
            ans+=n-1-temp;
            res=min(res, ans);
        }
        printf("%d\n", res);
    }
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章