归并排序求逆序对数

时间:2022-05-31 09:52:29
UPC OJ

Problem H: Seating Chart

Description

Bilbo's birthday is coming up, and Frodo andSam are in charge of all the party planning! Theyhave invited all the hobbits of Middle Earth tothe party, and everyone will be sitting in a single row at anextremely long dining table.

However, due to poor communication, Frodo andSam have each independently put together a

seating chart for all the hobbits at thedining table. Help Frodo and Sam and out how similar theirseating charts are by counting the total numberof distinct pairs of hobbits who appear in different orders in thetwo charts.

Input

The input file will contain multiple testcases. Each test case begins with a single line containingan integer N (1<=N<=100,000) indicating the numberof hobbits. The next two lines represent Frodo's and Sam's seatingcharts, respectively. Each seating chart is specified as a singleline of N unique alphabetical strings;the set of strings in each line are guaranteed to be identical. Theend-of-input is denoted by a line containing the number0.

Output

For each input test case, output a singleinteger denoting, out of the N  choose 2 distinctpairs ofhobbits, how many pairs appearin different orders in Frodo's and Sam's seatingarrangements.

Sample Input

3 Frodo Sam Bilbo Sam Frodo Bilbo 5 A B C D E B A D E C0

Sample Output

1 3

 题目让求有多少对逆序数(字符串),有一种方法是归并排序(时间复杂度O(nlogn))。
 归并排序先把序列分成元素个数尽量相等的两半,把两边元素分别排序,再把两个有序表合成一个。怎么合成一个呢?建一个新临时辅助表,只要分别找到两个序列最小的那个,把这两个最小的再比,删除小的那个加到新表中。。。再重复这个步骤(每次删除一个最小的元素,加到新表中)。。直到两个序列都空了就排序完成。最后把这个临时数组复制到原来的的数组中就行了~

归并排序:

 设T是临时空间,x,y代表A的范围[x,y)。
   void mergesort(int *A,int x,int y,int *T)
    {
       if(y-x>1)  //如果序列非空
       {
           int m=x+(y-x)/2;  //如果序列长度为奇数 左边序列就比右边少一个如果是偶数就一样
           int p=x,q=m,i=x; //p和q是当前左右序列第一个元素的位置(最小的那个元素)
           mergesort(A,x,m,T); //递归排序
           mergesort(A,m,y,T);
           while(p<m||q<y) //只要有一个序列非空就继续合并
           {
               if(q>=y||p<=A[q]) T[i++]=A[p++];//如果右边空或者左边非空且左边第一个元素小于右边第一个,就把左边那个加到临时空间T里
               else T[i++]=A[q++]; //反之加右边那个
           }
           for(i=x; i<y;i++) A[i]=T[i]; //把排序好的T复制回A,注意A的范围是[x,y)
       }
    }

 接下来就是求逆序对数了。我们发现如果没有逆序对数,那么合并的时候一定是左边合并完了才开始合并右边。如果左边还有元素,而却从右边加元素到T,说明左边那些元素都比它大,也就是它和左边每个元素都构成逆序对数!所以这时答案增加m-p。直到最后归并排序结束,得到最终答案。

 这道题还有个问题,题目给的是字符串,不是直接给的数字,那怎么做呢?其实开始我也不会(每次去找这个字符串的位置,然后strcpy,果断超时
归并排序求逆序对数),问了学长才知道,原来还有#include 这种东西。。大概就是映射吧,好高端=。= 比如map h  h是int类型的,对应那个string。

代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<map>
using namespace std;
int a[100010],T[100010];
char tt[100000];
int N;
long long cnt;
void merge_sort(int *A,int x,int y,int *T)
{
    if(y-x>1)
    {
        int m=x+(y-x)/2;
        int p=x,q=m,i=x;
        merge_sort(A,x,m,T);
        merge_sort(A,m,y,T);
        while(p<m||q<y)
        {
            if(q>=y||p<m&&A[p]<=A[q]) T[i++]=A[p++];
            else
            {
                T[i++]=A[q++];
                cnt+=m-p;
            }
        }
        for(i=x; i<y; i++) A[i]=T[i];
    }
}

int main()
{
    map<string,int> h;
    while(scanf("%d",&N),N)
    {
        int i;
        for(i=0; i<N; i++)
        {
            scanf("%s",tt);
            h[tt]=i;
        }
        for(i=0; i<N; i++)
        {
            scanf("%s",tt);
            a[i]=h[tt];
        }
        cnt=0;
        merge_sort(a,0,N,T);
        printf("%lld\n",cnt);
    }
    return 0;
}