1. 程式人生 > >ACM ICPC 2011–2012, NEERC, Northern Subregional Contest J. John’s Inversions(合併排序求逆序數對數)

ACM ICPC 2011–2012, NEERC, Northern Subregional Contest J. John’s Inversions(合併排序求逆序數對數)

題目連結:http://codeforces.com/gym/100609/attachments 題目大意:有n張牌,每張牌有紅色和藍色兩面,兩面分別寫了一些數字,同種顏色的任意兩個數字若排在前面的數字比排在後面的數字大就叫做一對逆序數。求怎樣排序得到的逆序數對最少。 解題思路:其中一種顏色的數字是順序且這種顏色數字相同時對應的另一種顏色的數字是順序時得到的逆序數對數最少。難點在於求逆序數對數。因為數量很大O(n^2)複雜度不能滿足,這裡根據合併排序的原理求解每個數字前面有多少個比它大的數字,最後加起來就是答案。合併排序複雜度是O(n)。 程式碼如下:
#include <cstdio>
#include <algorithm>
#include <cstring>
#define ll long long
using namespace std; 
const int maxn=100005;
ll ans;
int data[maxn];
int n;
struct node
{
    int a,b;
}s[maxn];
ll cmp(node p,node q) 
{
    if(p.a==q.a)
        return p.b<q.b;
    else
        return p.a<q.a;
}
void Merge(int l,int mid,int r)  //合併
{
    int k=0,i=l,j=mid+1;   
    int tar[maxn];    //臨時陣列用來暫時儲存有序數列
    while(i<=mid&&j<=r)   //把兩組有序數列a[l]...a[mid]和a[mid+1]...a[r]合併成一組有序數列tar[0]...tar[r-l+1]
    {
        if(data[i]<=data[j])   //把小的數字給陣列tar[]
            tar[k++]=data[i++];
        else                  
        {
            tar[k++]=data[j++];
            ans+=mid-i+1;     //關鍵點,在後一組裡有最小的數字,說明前一組序列裡剩下的數字都比這個數字大,而且前一組數列裡的每個數字都在後一組數列的前面
        }
    }
    while(i<=mid)
        tar[k++]=data[i++];
    while(j<=r)
        tar[k++]=data[j++];
    for(k=0,i=l;i<=r;)       //把有序數列複製給data[l]..data[r],data[]值是改變的,變成有序的,保證合併上一層有序數列時滿足條件
        data[i++]=tar[k++];
}
void MergeSort(int l,int r)  //排序
{
    if(l<r)
    {
        int mid=(l+r)/2;
        MergeSort(l,mid);
        MergeSort(mid+1,r);
        Merge(l,mid,r);
    }
}
int main(void)
{
    while(scanf("%d",&n)!=EOF)
    {
        memset(data,0,sizeof(data));
        for (int i=0;i<n;i++)
            scanf("%d%d",&s[i].a,&s[i].b);
        sort(s,s+n,cmp);
        for(int i=0;i<n;i++)
            data[i]=s[i].b; 
        ans=0;
        MergeSort(0,n-1);
        printf("%I64d\n",ans);
    }
}