和归并排序一样,划分和递归求解都好理解,关键在于合并:如何求出i在左边,而j在
右边的逆序对数目呢?统计的常见技巧是“分类”。下面按照j的不同把这些“跨越两边”的逆序
对进行分类:只要对于右边的每个j,统计左边比它大的元素个数f(j),则所有f(j)之和便是答
案。
幸运的是,归并排序可以“顺便”完成f(j)的计算:由于合并操作是从小到大进行的,当
右边的A[j]复制到T中时,左边还没来得及复制到T的那些数就是左边所有比A[j]大的数。此
时在累加器中加上左边元素个数m-p即可(左边所剩的元素在区间[p,m)中,因此元素个数
为m-p)。换句话说,在代码上的唯一修改就是把"else T[i++] = A[q++];"改成"else { T[i++] =
右边的逆序对数目呢?统计的常见技巧是“分类”。下面按照j的不同把这些“跨越两边”的逆序
对进行分类:只要对于右边的每个j,统计左边比它大的元素个数f(j),则所有f(j)之和便是答
案。
幸运的是,归并排序可以“顺便”完成f(j)的计算:由于合并操作是从小到大进行的,当
右边的A[j]复制到T中时,左边还没来得及复制到T的那些数就是左边所有比A[j]大的数。此
时在累加器中加上左边元素个数m-p即可(左边所剩的元素在区间[p,m)中,因此元素个数
为m-p)。换句话说,在代码上的唯一修改就是把"else T[i++] = A[q++];"改成"else { T[i++] =
A[q++]; cnt += m-p; }"。当然,在调用之前应给cnt清零。
#include<cstdio>
#include<stdlib.h>
#include<algorithm>
#include<string.h>
using namespace std;
int globa_count, n, len;
struct Mystring{
char s[200];
int num;
}mystring[200];
Mystring mystring2[200];
bool cmp(Mystring a, Mystring b){
return a.num<b.num; //如果改为<=则变为稳定排序
}
void merge_inversion(char* A, int x, int y, char* T){
if(y-x > 1){
int m = x + (y-x)/2; //划分
int p = x, q = m, i = x;
merge_inversion(A, x, m, T); //递归求解
merge_inversion(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++];
globa_count+=m-p;
} //从右半数组复制到临时空间
}
for(i = x; i < y; i++)
A[i] = T[i]; //从辅助空间复制回A数组
}
}
int main(){
scanf("%d%d", &len, &n);
for (int i=0; i<n; i++){
scanf("%s", mystring[i].s);
char t[200];char s[200];
mystring2[i]=mystring[i];
globa_count=0;
merge_inversion(mystring[i].s, 0, len, t);
mystring2[i].num=globa_count;
}
/*printf("\n");
for (int i=0; i<n; i++)
printf("%s\n", mystring2[i].s);
*/
sort(mystring2, mystring2+n, cmp);
/*
printf("\n");
*/
for (int i=0; i<n; i++)
printf("%s\n", mystring2[i].s);
return 0;
/*char a[5]={'C','B','C','D','E'};
char t[5];//{5,4,3,2,1};
//printf("caonima");
globa_count=0;
merge_inversion(a, 0, 5, t);
printf("%d", globa_count);*/
}