POJ 4020 NEERC John's inversion 贪心+归并求逆序对

题意:给你n张卡,每张卡上有蓝色和红色的两种数字,求一种排列使得对应颜色数字之间形成的逆序对总数最小

题解:贪心,先按蓝色排序,数字相同再按红色排,那么蓝色数字的逆序总数为0,考虑交换红色的数字消除逆序,POJ 4020 NEERC John's inversion 贪心+归并求逆序对

那么这个操作的代价是包含区间内的蓝色的点每个逆序数增加1,而红色的数字交互最多也只能消除那么多对逆序对。不会比当前更优。

因为数据范围比较大,用树状数组,线段树,可能要离散,容易被卡,所以学习了归并排序的方法来求逆序对总数。

思路大概是,归并之前的左右两个序列是已经排好序的,分别叫做L和R,如果L(i)>R(j),那么R(j)也一定比L(i+1)以后的小,就算一下逆序对。归并排序又是先把小的给并入,所以不会少计算逆序对。

#include<cstdio>

#include<algorithm>

using namespace std;



const int maxn = 100005;



struct Unit

{

  int r,b;

  bool operator < (const Unit& rhs) const {

    return r < rhs.r || ( r == rhs.r && b < rhs.b );

  }

}a[maxn];



int t[maxn];

long long cnt;

void merge_sort(int l,int r)

{

    if(l==r) return;

    int mid = l+r>>1;

    merge_sort(l,mid);

    merge_sort(mid+1,r);

    int i = l, j = mid+1, k =l,p;

    while(i <= mid && j <= r){

        if(a[i].b>a[j].b){

            cnt += mid-i+1;

            t[k] = a[j].b;

            j++;

        } else {

            t[k] = a[i].b;

            i++;

        }

        k++;

    }

    if(i == mid + 1) for(p = j; p <= r; p++) t[k++] = a[p].b;

    else for(p = i; p <= mid; p++) t[k++] = a[p].b;

    for(k = l;k <= r; k++) a[k].b = t[k];

}





int main()

{

    int n;

    scanf("%d",&n);

    for(int i = 0; i < n; i++){

        scanf("%d%d",&a[i].r,&a[i].b);

    }

    sort(a,a+n);

    cnt = 0;

    merge_sort(0,n-1);

    printf("%I64d",cnt);



    return 0;

}

 

你可能感兴趣的:(version)