逆序对定义:对于一个包含N个非负整数的数组A[1..n],如果有i < j,且A[ i ]>A[ j ],则称(A[ i] ,A[ j] )为数组A中的一个逆序对。
常见的两种方法求解逆序对:1.穷举法(暴力求解),时间复杂度O(n^2)。2.归并法, 时间复杂度O(nlogn)。
穷举法:对于一个给定的序列,依次从左往右取每一个元素,从该元素右边第一个元素开始向右扫描,遇到比它小的元素,则计数+1,直到处理完整个序列。
- #include <iostream>
- #include <cstdlib>
- #include <cstdio>
-
- using namespace std;
-
- #define MAXN 1000
- int num[MAXN];
- int sum = 0;
-
- void solve(int n)
- {
- for(int i = 0; i < n; i++)
- for(int j = i+1; j < n; j++)
- {
- if(num[i] > num[j])
- sum++;
- }
- }
-
- int main(int argc, char const *argv[])
- {
- int n;
- scanf("%d", &n);
- for(int i = 0; i < n; i++)
- scanf("%d", &num[i]);
- solve(n);
- printf("%d\n", sum);
- return 0;
- }
归并法:将序列A[l..r]分成两半A[l..mid]和A[mid+1..r]分别进行归并排序,然后再将这两半合并起来。
在合并的过程中(设l<=i<=mid,mid+1<=j<=r),当A[i]<=A[j]时,不产生逆序数;当A[i]>A[j]时,在
前半部分中比A[i]大的数都比A[j]大,所以,将A[j]放在A[i]前面的话,逆序数要加上mid+1-i。因此,可以在归并排序中的合并过程中计算逆序数。
- #include <iostream>
- #include <cstdlib>
- #include <cstdio>
-
- using namespace std;
-
- #define MAXN 1000
- int num[MAXN], temp[MAXN];
- int sum = 0;
-
- void Merge(int l, int mid, int r)
- {
- int i = l, j = mid+1, k = l;
- while(i <= mid && j <= r)
- {
- if(num[i] > num[j])
- {
- temp[k++] = num[j++];
- sum += mid-i+1;//产生逆序对
- }
- else
- temp[k++] = num[i++];
- }
- while(i <= mid)
- temp[k++] = num[i++];
- while(j <= r)
- temp[k++] = num[j++];
- for(int i = l; i <= r; i++)
- num[i] = temp[i];
-
- }
-
- void Merge_sort(int l, int r)
- {
- if(l < r)
- {
- int mid = l + (r-l)/2;//可防止加法溢出
- Merge_sort(l, mid);//左边
- Merge_sort(mid+1, r);//右边
- Merge(l, mid, r);//合并
- }
-
- }
-
-
- int main(int argc, char const *argv[])
- {
- int n;
- scanf("%d", &n);
- for(int i = 0; i < n; i++)
- scanf("%d", &num[i]);
- Merge_sort(0, n-1);
- printf("%d\n", sum);
- return 0;
- }
附上POJ逆序对的一道测试题:http://poj.org/problem?id=1804