https://vjudge.net/problem/HDU-4911
Inversion
bobo has a sequence a1,a2,…,an. He is allowed to swap two adjacent numbers for no more than k times.
Find the minimum number of inversions after his swaps.
Note: The number of inversions is the number of pair (i,j) where 1≤i<j≤n and ai>aj.
Input
The input consists of several tests. For each tests:
The first line contains 2 integers n,k (1≤n≤10^5,0≤k≤10^9). The second line contains n integers a1,a2,…,an (0≤ai≤10^9).
Output
For each tests:
A single integer denotes the minimum number of inversions.
Sample
Input | Output |
---|---|
3 1 2 2 1 3 0 2 2 1 | 1 2 |
解题思路
归并排序。
首先我们想想暴力,直接一个一个数求逆序对数,并统计总数,这样时间复杂度为O(n^2),超时。
优化:我们利用归并排序,把一组大数据分成两组小数据,一直分,最后直到分到每组只有一个元素,这时候开始两两排序合并,如果a[i]>a[j](i<j),代表这是逆对数。
然而,每组数据都是有序的,这就代表如果a[i]>a[j](i<j),他有mid-i+1个逆序对数。直到归并完成,我们就统计出有多少个逆序对数了。
在统计过后,我们试着思考一下:
cnt<=k 代表总逆序对数不够交换k次,所以答案为0。
cnt>k 代表k次相邻的交换,剩余的逆序对数为cnt-k。
归并排序
归并排序是一种基于分治思想的排序算法。它将一个数组拆分成两个子数组,然后对子数组进行递归排序,最后将排好序的子数组合并成一个有序数组。
具体步骤如下:
- 将数组拆分成两个子数组,直到每个子数组只有一个元素为止。
- 对每个子数组进行递归排序,直到所有子数组都排好序。
- 将排好序的子数组合并成一个有序数组。
在合并子数组时,需要比较两个子数组中的元素,将较小的元素放入临时数组中,直到其中一个子数组的元素全部放入临时数组。然后将另一个子数组的剩余元素放入临时数组。最后,将临时数组中的元素复制回原数组的对应位置。
归并排序的时间复杂度是O(nlogn),空间复杂度是O(n)。它是一种稳定的排序算法,适用于大部分情况。由于它需要使用额外的空间来存储临时数组,所以在排序大规模数据时,可能会占用较多的内存。
题解
#include <iostream>
#include <algorithm>
using namespace std;
int a[100005],b[100005];
long long k,n,cnt; //cnt可能超过int表示范围
void Merge(int l,int mid,int r){
int i=l,j=mid+1,t=0;
while(i<=mid&&j<=r){
if(a[i]>a[j]){
cnt+=mid-i+1;
b[t++]=a[j++];
}
else b[t++]=a[i++];
}
while(i<=mid) b[t++]=a[i++];
while(j<=r) b[t++]=a[j++];
for(i=0;i<t;i++) a[l+i]=b[i];
}
void Mergesort(int l,int r){
if(l<r){
int mid=(l+r)/2;
Mergesort(l,mid);
Mergesort(mid+1,r);
Merge(l,mid,r);
}
}
int main(){
while(scanf("%d %d",&n,&k)!=EOF){
cnt=0;
for(int i=0;i<n;i++) scanf("%d",a+i);
Mergesort(0,n-1);
if(cnt<=k) printf("0\n");
else printf("%lld\n",cnt-k);
}
return 0;
}