目录
题目描述
现在给你一个由n个互不相同的整数组成的序列,现在要求你任意交换相邻的两个数字,使序列成为升序序列,请问最少的交换次数是多少?
输入
输入包含多组测试数据。每组输入第一行是一个正整数n(n<500000),表示序列的长度,当n=0时,表示输入结束。
接下来的n行,每行一个整数a[i](0<=a[i]<=999999999),表示序列中第i个元素。
输出
对于每组输入,输出使得所给序列升序的最少交换次数。
样例输入
5
9
1
0
5
4
3
1
2
3
0
样例输出
6
0
问题分析
从题中看到关键点①相邻元素交换②排序(升序)求交换次数
根据示例可以知道要对数组进行排序,可以用冒泡排序,但是冒泡排序O(n^2)时间超限制
从这里可以看出,归并排序是最适合解该题的算法
仔细分析该题,其实是求逆序对的个数
逆序:一般认为从左向右序列的数字增大认为是正序的,那么从左到右序列的序列数字出现减小就认为是逆序的。一个“逆序”的数学定义是这样的,如果存在正整数 i, j 使得 1 ≤ i < j ≤ n 而且 A[i] > A[j],则 <A[i], A[j]> 这个有序对称为 A 的一个逆序,又称作一个逆序对。
逆序对:数学术语,设 A 为一个有 n 个数字的有序集 (n>1),其中所有数字各不相同。如果存在正整数 i, j 使得 1 ≤ i < j ≤ n 而且 A[i] > A[j],则 <A[i], A[j]> 这个有序对称为 A 的一个逆序对,也称作逆序数
逆序列:给定n个数1,2,...,n的一个排列a1a2...an,令bi是数i在此排列中的逆序数,换句话说,bi等于该排列中先于i又大于i的那些数的个数。数列b1b2...bn称为排列a1a2...an的逆序数列(inversion sequence)。排列与逆序数列一一对应。例如排列32541的逆序数列是01014。解释如下:b5是4的原因为a5是1,它的前面有3、2、5、4,他们都大于1,所以有4个数大于1。b3是0的原因是a是5,它的前面有3、2,他们都小于5,所以有0个数大于5
归并排序可参考排序算法——归并排序_赚钱去流浪的博客-CSDN博客
代码
import java.util.Scanner;
/**
* @description:
* @author: gyj
* @date: 2022/8/29 22:04
*/
public class Main {
public static Long num;
public static int[] arr;
public static int[] brr = new int[500050];
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
while (sc.hasNext()) {
int n = sc.nextInt();
arr = new int[n];
if (n==0 || n>=500000){
break;
}
num = 0l;
for (int i = 0; i < arr.length; i++) {
arr[i] = sc.nextInt();
}
mergeSort(arr, 0,n-1);
System.out.println(num);
continue;
}
}
public static void merge(int a[],int start,int mid,int end) {
int i=start,j=mid+1,k=start;
while(i<=mid&&j<=end) {
if(a[i]<=a[j]) {
brr[k++]=a[i++];
}
else {
num+=j-k;
brr[k++]=a[j++];
}
}
while(i<=mid) {
brr[k++]=a[i++];
}
while(j<=end) {
brr[k++]=a[j++];
}
for(i=start;i<=end;i++,k++) {
a[i]=brr[i];
}
}
public static void mergeSort(int a[],int start,int end) {
if(start<end) {
int mid=(start+end)/2;
mergeSort(a,start,mid);
mergeSort(a,mid+1,end);
merge(a,start,mid,end);
}
}
}