给定一个1-N的排列A1, A2, … AN,如果Ai和Aj满足i < j且Ai > Aj,我们就称(Ai, Aj)是一个逆序对。
求A1, A2 … AN中所有逆序对的数目。
Input
第一行包含一个整数N。
第二行包含N个两两不同整数A1, A2, … AN。(1 <= Ai <= N)
对于60%的数据 1 <= N <= 1000
对于100%的数据 1 <= N <= 100000
Output
一个整数代表答案
Sample Input
5
3 2 4 5 1
Sample Output
5
思路:
网上了解到要用归并排序(然而归并排序是个啥{{{(>_<)}}}),交换的次数就是最后的答案,学了两天,终于写出来了。注释还是蛮详细的。
下面是代码:
#include<iostream>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<list>
#include<iterator>
#include<stack>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
//#define e 2.718281828459
#define INF 0x7fffffff
#pragma warning(disable:4996)
#define sf scanf
#define pf printf
//#define max(a,b) (a)>(b)?(a):(b);
//#define min(a,b) (a)<(b)?(a):(b);
#define pi acos(-1.0);
#define eps 1e-9;
#define MAX 1000000+10
#include <queue>
void MergeSort(int ar[], int len, int sum[]);
void Merge(int left[], int lena, int right[], int lenb, int temp[]);
ull ans;
int a[100000];
int temp[100000];//记录每一次的段排序,减去了在link()函数中动态申请的时间
int main(void) {
int n;
while (sf("%d", &n) != EOF) {
ans = 0;
for (int i = 0; i < n; i++) sf("%d", &a[i]);
MergeSort(a, n, temp);//将a一分为二a[1]....a[9],a[10]......a[20]
pf("%lld\n", ans);
}
/*system("pause");*/
return 0;
}
void Merge(int left[], int left_len, int right[], int right_len,int temp[]) {
/*
函数功能:将排好序的left段和right段按排好序的状况连接,他们的状况保存在temp数组中
*/
int i=0, j=0, k=0;
while (i < left_len&&j < right_len) {
if (left[i] > right[j])
temp[k++] = right[j++], ans += left_len - i;//从left[i]到left[left_len-1]都可以和right[j]交换,因为left[i]到left[left_len]为升序排列
//错误:temp[k++] = right[j++],ans++;只要right部分的往前添,就相当于交换,从右边交换到了左边
else
temp[k++] = left[i++];
}
//错误:if( i<left_len) ans = ans + (left_len - i-1) * right_len;//只要left部分的往后添,就相当于交换
while (i < left_len) {
temp[k++] = left[i++];
}
while (j < right_len)
temp[k++] = right[j++];
for (int i = 0; i < left_len + right_len; i++) {//将排序好的段重新输入原数组,因为之后temp数组还要一直变化,直到最后一次即21个元素都排好序时就和原数组一样了
left[i] = temp[i];//之前没有将排序好的段输入数组中导致递归时temp数组被改动,无法得到正确结果
}
}
void MergeSort(int ar[],int len,int sum[]) {
if (len == 1)
return ;
else {
int lenl = (len+1)>>1;
int lenr = len - lenl;
MergeSort(ar, lenl,sum);
MergeSort(ar+lenl, lenr, sum+lenl);
Merge(ar, lenl, ar+lenl, lenr, sum);
}
}
下面是整理过的代码(其实也就是短了点儿,还是看上面吧):
#include<iostream>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<list>
#include<iterator>
#include<stack>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
#define e 2.718281828459
#define INF 0x7fffffff
#pragma warning(disable:4996)
#define sf scanf
#define pf printf
#define max(a,b) (a)>(b)?(a):(b);
#define min(a,b) (a)<(b)?(a):(b);
#define pi acos(-1.0);
#define eps 1e-9;
#define MAX 1000000+10
#include <queue>
ull ans;
int a[100000];
int temp[100000];//记录每一次的段排序,减去了在Merge()函数中动态申请的时间
void Merge(int left[], int left_len, int right[], int right_len, int temp[]) {
//函数功能:将排好序的left段和right段按排好序的状况连接,他们的状况保存在temp数组中
int i = 0, j = 0, k = 0;
while (i < left_len&&j < right_len) {
if (left[i] > right[j]) temp[k++] = right[j++], ans += left_len - i;//从left[i]到left[left_len-1]都可以和right[j]交换,因为left[i]到left[left_len]为升序排列
else temp[k++] = left[i++];
}
while (i < left_len)
temp[k++] = left[i++];
while (j < right_len)
temp[k++] = right[j++];
for (int i = 0; i < left_len + right_len; i++) //将排序好的段重新输入原数组,因为之后temp数组还要一直变化,直到最后一次即21个元素都排好序时就和原数组一样了
left[i] = temp[i];//之前没有将排序好的段输入数组中导致递归时temp数组被改动,无法得到正确结果
}
void MergeSort(int ar[], int len, int sum[]) {
if (len == 1) return;
else {
int lenl = (len + 1) >> 1;
int lenr = len - lenl;
MergeSort(ar, lenl, sum);
MergeSort(ar + lenl, lenr, sum + lenl);
Merge(ar, lenl, ar + lenl, lenr, sum);
}
}
int main(void) {
int n;
while (sf("%d", &n) != EOF) {
ans = 0;
for (int i = 0; i < n; i++) sf("%d", &a[i]);
MergeSort(a, n, temp);//将a一分为二a[1]....a[9],a[10]......a[20]
pf("%lld\n", ans);
}
return 0;
}