题目链接 http://acm.hdu.edu.cn/showproblem.php?pid=4911
Problem Description
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≤105,0≤k≤109). The second line contains n integers a1,a2,…,an (0≤ai≤109).
Output
For each tests:
A single integer denotes the minimum number of inversions.
Sample Input
3 1 2 2 1 3 0 2 2 1
Sample Output
1 2
题目大意: 给定一个无序数列,每次可以交换相邻2个数,最多交换k次,交换之后逆序对数最少为多少。
题解: 每次交换最多减少1对逆序对,设逆序对数为n,则结果为max(n,0)
求逆序对数可以归并排序,树状数组。
归并排序就是先把数组从中间拆开看作2半,假设一个递归函数mergeSort()可以将一个数组归并排序并返回数组中的逆序对数,则分别对左右两半数组mergeSort,求出左右两边的逆序对数之后。
由于逆序对只有3种情况: 2个数都在左边数组或者右边数组,一个数在左边一个数在右边。
前面2种情况已经算出来了,第三种情况在左右数组合并的时候统计即可。
统计方法,在合并数组的时候,本来右边的数组应该都比左边大(升序的归排),如果右边一个数比左边小,则它们是逆序对,而且此时右边的这个数一定也比左数组那个数右边的那些数小,所以它们都是逆序对,通过这样的方法可以算出逆序对数。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;
int num[100010];
int n;
ll k;
/**
* 数组两路合并
*/
ll merge(int *a,int start,int end,int mid) {
int count = end - start + 1; // 数组元素个数
int *temp = new int[count+2]; // 临时数组
int l = start;
int r = mid+1;
int p = 0;
ll ret = 0; // 返回值
while(l <= mid && r <= end) {
if(a[l] <= a[r]) {
temp[p++] = a[l++];
} else {
temp[p++] = a[r++];
ret += (mid-l+1); // 统计逆序对数
}
}
while(l <= mid) {
temp[p++] = a[l++];
}
while(r <= mid) {
temp[p++] = a[r++];
}
p = 0;
for (int i = start;i <= end ; i++) {
a[i] = temp[p++];
}
delete[] temp;
return ret;
}
/**
* 归并排序
*/
ll mergeSort(int *a,int start,int end) {
if(start >= end)
return 0;
int mid = (start+end) / 2;
ll l =mergeSort(a,start,mid); // 左边排序
ll r =mergeSort(a,mid+1,end); // 右边排序
ll m = merge(a,start,end,mid); // 左右两边合并
return m+l+r;
}
int main()
{
while(scanf("%d%lld",&n,&k) != EOF) {
for (int i = 0; i < n; i++) {
scanf("%d",num+i);
}
ll number = mergeSort(num,0,n-1);
ll ret = number - k;
if(ret < (ll)0)
ret = (ll)0;
printf("%lld\n",ret);
}
return 0;
}