题目
给定一个长度为 的整数数列,请你计算数列中的逆序对的数量。
逆序对的定义如下:对于数列的第 个和第 个元素,如果满足 且 ,则其为一个逆序对;否则不是。
输入格式
第一行包含整数 ,表示数列的长度。
第二行包含 个整数,表示整个数列。
输出格式
输出一个整数,表示逆序对的个数。
数据范围
,
数列中的元素的取值范围 。
输入样例:
6
2 3 4 5 6 1
输出样例:
5
解题
方法一:分治 归并排序
思路
归并排序。
首先我们认为 merge_sort(l, r)
这个函数能把序列 区间排好序并返回排序前逆序对的数量。
考虑分治,将所有逆序对分为三种情况:
-
左半边内部的逆序对数量:
merge_sort(l, mid)
。 -
右半边内部的逆序对数量:
merge_sort(mid + 1, r)
。 -
分别在左右两边的逆序对:
设在归并时,,那么对于右半边中的每个元素(
nums[j]
),左半边元素(nums[i]
)中大于它的数的数量就是它能形成的逆序对的数量,记作 。也就是说,当 时,。
代码
import java.util.*;
import java.io.*;
public class Main {
static int[] nums, tmp;
public static void main(String[] args) throws IOException {
StreamTokenizer in = new StreamTokenizer(new BufferedReader(new InputStreamReader(System.in)));
in.nextToken();
int n = (int) in.nval;
nums = new int[n];
tmp = new int[n];
for (int i = 0; i < n; ++i) {
in.nextToken();
nums[i] = (int) in.nval;
}
System.out.println(mergeSort(0, n - 1));
}
static long mergeSort(int l, int r) {
if (l >= r) return 0L;
int mid = l + r >> 1;
long cnt = mergeSort(l, mid) + mergeSort(mid + 1, r);
int i = l, j = mid + 1, k = 0;
while (i <= mid && j <= r) {
if (nums[i] <= nums[j]) tmp[k++] = nums[i++];
else {
tmp[k++] = nums[j++];
cnt += mid - i + 1;
}
}
while (i <= mid) tmp[k++] = nums[i++];
while (j <= r) tmp[k++] = nums[j++];
for (i = l, j = 0; i <= r; ++i, ++j) nums[i] = tmp[j];
return cnt;
}
}
#include <iostream>
using namespace std;
typedef long long ll;
const int N = 1e5 + 10;
int n;
int q[N], tmp[N];
ll merge_sort(int q[], int l, int r) {
if (l >= r) return 0L;
int mid = l + r >> 1;
ll cnt = merge_sort(q, l, mid) + merge_sort(q, mid + 1, r);
int i = l, j = mid + 1, k = 0;
while (i <= mid && j <= r) {
if (q[i] <= q[j]) tmp[k++] = q[i++];
else {
tmp[k++] = q[j++];
cnt += mid - i + 1;
}
}
while (i <= mid) tmp[k++] = q[i++];
while (j <= r) tmp[k++] = q[j++];
for (i = l, j = 0; i <= r; ++i, ++j) q[i] = tmp[j];
return cnt;
}
int main () {
scanf("%d", &n);
for (int i = 0; i < n; ++i) scanf("%d", &q[i]);
printf("%lld", merge_sort(q, 0, n - 1));
return 0;
}
时间复杂度:。
评论区