HDOJ4911Inversion---归并排序求逆序对数

探讨了在给定无序数列中,通过有限次数的相邻元素交换,寻找最小逆序对数量的问题。利用归并排序算法进行逆序对计数,并结合树状数组优化计算过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目链接 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;
} 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值