HDU-P4911:Inversion[归并排序]
HDU-P4911:Inversion[归并排序]
题目
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
题目大意
输入第一行给出需处理数据的个数和处理次数的限制,第二行给出数据。
输出是数据求出经过处理后,这组数据中最少的逆序列 1的个数。
根据题意,我们可以对相邻的一对数字进行交换,显然每一次交换我们都可以使逆序列的个数减少1,所以我们要求解的问题与接下来的问题等价:寻找给出数据中逆序列的个数。
首先容易想到的暴力解法显然会超时,我们需要一个时间复杂度更优的解法,若对一个局部有序的数据进行比较即可大大缩短求解所需的时间,归并排序就可以实现类似的功能,只需要在归并排序中加入一行计数的代码即可。
对代码的合理性解释如下:Merge实现了计算两个有序组间可以组成逆序列的对数,由于递归程序是自小问题向大问题运行的,故在最大的分组计算有序组间逆序列个数之前,各个有序的组内逆序列的个数都已经计算出,故本组内顺序的调整也并不会影响计算结果的正确性。
代码
#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
const int N = 5e5;
int a[N], tmp[N];
long long cnt;
void Merge(int s, int m ,int e) {
int p = 0, i = s, j = m+1;
while(i <= m && j <= e) {
if(a[i] <= a[j])
tmp[p++] = a[i++];
else {
cnt += j-s+p; //计数代码,实现了计算分成的两组之间的逆序列的计算
tmp[p++] = a[j++];
}
}
while(i<=m) tmp[p++] = a[i++];
while(j<=e) tmp[p++] = a[j++];
for(int i = s; i <= e; ++i) {
a[i] = tmp[i-s];
}
}
void Mergesort(int s, int e) {
if(s < e) {
int mid = (s+e)/2;
Mergesort(s, mid);
Mergesort(mid+1, e);
Merge(s, mid, e);
}
}
int main(void)
{
int n, k;
while(~scanf("%d%d", &n, &k)) {
for(int i = 0; i < n; ++i) {
scanf("%d", &a[i]);
}
cnt = 0;
Mergesort(0, n-1);
printf("%lld\n", cnt>k?cnt-k:0); //给出的k值可能会大于实际需要的次数
}
return 0;
}
题目的代码难度不大,主要难度存在于对题目的理解和解法的思考。代码思路是借鉴了很多大佬的思路,如对代码有任何建议或意见,欢迎探讨。
对于一组数字a[n],如果存在i<j并且a[i]>a[j],则称其为一组逆序列。 ↩︎
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 按钮权限的设计及实现