HDU-1394(线段树|归并排序)

来源:互联网 发布:三国无双Mac版 编辑:程序博客网 时间:2024/06/04 18:39

Minimum Inversion Number

题目意思是给你一个数组, 让你求数字对 (a[i],a[j]) 满足i

归并排序的思想

假设排序是从小到大的,当右边的A[j]复制到T中时, 左边的还没来得及复制到T中的那些数就是左边所有比A[j]大的数,因此,在累加器中加上左边元素个数即可。
求出第一个序列的逆序对之后可以递推推出其他序列的逆序对,取最大值,具体操作可以看代码

#include <cstdio>#include <cstring>using namespace std;const int N = 6000;int a[N], b[N], cnt, c[N];void MergeSort(int *A, int l, int r, int *T) {//    printf("l == %d, r == %d\n", l, r);    if(r - l > 1) {        int m = l + (r - l)/2;        int p = l, q = m, i = l;        MergeSort(A,l,m,T);        MergeSort(A,m,r,T);        while(p < m || q < r) {            if(q >= r || (p < m && A[p] <= A[q])) T[i++] = A[p++];            else T[i++] = A[q++], cnt += m - p;        }        for (i = l; i < r; i++) A[i] = T[i];    }}int main() {    int n;    while(scanf("%d", &n) != EOF) {        cnt = 0;        for (int i = 0; i < n; i++) {            scanf("%d", a+i);            c[i] = a[i];        }        MergeSort(a,0,n,b);        int mx = cnt;//        printf("cnt == %d\n", cnt);        for (int i = 0; i < n-1; i++) {            cnt = cnt - c[i] + n - c[i] - 1;            if(mx > cnt) mx = cnt;        }        printf("%d\n", mx);    }    return 0;}

以上代码中p属于[l,m),q属于[m,r)。p所在区间下标一定小于q所在区间下标。当A[p]>A[q],由于A[p]->A[m-1]是按照从小到大的顺序开始排的。所以,当最小的值都大于A[q]。所以区间[p,m)是满足逆序对的条件的.直接在最后方加上m-p即可。

线段树方法

先看代码
#include <iostream>#include <cstring>#include <algorithm>#define lson l, m, rt<<1#define rson m+1,r,rt<<1|1using namespace std;const int INF = 0x3f3f3f3f;const int maxn = 5555;int SegTree[maxn << 2];void PushUP(int rt) {    SegTree[rt] = SegTree[rt<<1] + SegTree[rt<<1|1];}void updata(int p, int l, int r, int rt) {    if(l == r) {        SegTree[rt]++;        return ;    }    int m = (l + r) >> 1;    if(p <= m) updata(p,lson);    else updata(p,rson);    PushUP(rt);}int query(int L, int R, int l, int r, int rt) {    if(L <= l && r <= R)        return SegTree[rt];    int m = (l + r) >> 1;    int ret = 0;    if(L <= m) ret += query(L,R,lson);    if(R >  m) ret += query(L,R,rson);    return ret;}int main() {    int n;    int sum[maxn];    while(cin >> n) {//        build(0,n-1,1);        memset(SegTree,0,sizeof(SegTree));        int ans = 0,ret = INF;        for (int i = 0; i < n; i++) {            cin >> sum[i];            ans += query(sum[i],n-1,0,n-1,1);            updata(sum[i],0,n-1,1);        }        for (int i = 0; i < n-1; i++) {            ans += n - sum[i] - sum[i] - 1;            ret = min(ans,ret);        }        cout << ret << endl;    }    return 0;}

由于线段树是按照顺序更新的,后面更新线段树的下标一定比之前更新的下标是要大的,所以只要查看当前位置往后查看已经更新的数字数目就可以知道,逆序对的对数。

阅读全文
0 0
原创粉丝点击