HDU 4911 逆序数对

Tag: #hdu4911 #逆序数对

题目(原题目:HDU 4911)

bobo has a sequence a 1,a 2,…,a n. 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≤ia j.

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 a 1,a 2,…,a n (0≤a i≤10 9).

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

​ 本题的要点是求一个数列的逆序数(对),我们先来看一下逆序数对的简单定义:

在一个排列中,如果一对数的前后位置与大小顺序相反,即前面的数大于后面的数,那么它们就称为一个逆序。一个排列中逆序的总数就称为这个排列的逆序数

如2431中,21,43,41,31是逆序,逆序数是4。

​ 那么该如何求逆序对

方法如下:

考察每一位,判断从这一位往后有多少小于该位的,结果累加,得到最后结果。

例如,2,4,3,1 先判断2,之后有1是小于2的,结果+1,再判断4,之后3,1都小于4,结果+2,判断3时,结果再+1,最后得到结果4.

​ 很好理解,那么如何使用算法实现:当然,我们可以用两重循环暴力求解,但是显而易见O(x2)的复杂度非常高;更高效的方式有:归并排序、树状数组、线段树。

​ 本文为了迎合课上学习的归并排序,所以选择归并排序算法来解。

​ 归并排序:主要思想是将整个序列分成两部分,分别递归将这两部分排好序之后,再和并为一个有序的序列。

​ 在合并的过程中是将两个相邻并且有序的序列合并成一个有序序列,如以下一个无序列和分成的两个有序序列

Seq: 3  4  5  2  6  8  9

Seq1:3  4  5

Seq2:2  6  8  9

合并成一个有序列:

Seq*:2  3  4  5  6  8  9

​ 对于序列seq1中的某个数a[i],序列seq2中的某个数a[j],如果a[i]a[j],那么逆序数为seq1中a[i]后边元素的个数(包括a[i]),即len1-i;

​ 这样累加每次递归过程的逆序数,在完成整个递归过程之后,最后的累加和就是逆序的总数。不难理解。

​ 题中还提到只可交换相邻的数字,这里引入一个逆序数定理

逆序数定理,当逆序对数大于0时,若ak

​ 这个其实也不难理解。

完整代码

#include
#include

using namespace std;

#define ll long long

ll cnt ;

void Merge(int* A, int p, int q, int r) //根据算法导论改写
{
    int n1 = q - p + 1;
    int n2 = r - q;

    int i, j, k;

    int *L = new int[n1 + 1];
    int *R = new int[n2 + 1];

    for(i= 0;i< n1;i++)
    {
        L[i] = A[p + i];
    }
    for(j= 0;j< n2;j++)
    {
        R[j] = A[q + j + 1];
    }

    L[n1] = INT_MAX;//书中的哨兵牌,表示正无穷,这里用INT_MAX来代替。
    R[n2] = INT_MAX;


    for(i= 0,j= 0,k= p;k<= r;k++)
    {
        if (L[i]<=R[j])
        {
            A[k]= L[i];
            i++;
        }
        else //L[i]>R[j]出现逆序
        {
            A[k]= R[j];
            j++;
            cnt += (n1 - i); //根据逆序数定理添加的代码
        }
    }
}

void MergeSort(int* A,int p,int r)
{
    if(p< r)
    {
        int q = (p+ r)/2;
        MergeSort(A,p,q);
        MergeSort(A,q+1,r);
        Merge(A,p,q,r);
    }
}

int main()
{
    /*int n, k, A[1000000];//n<=10^5
    while(cin>>n>>k)
    {
        cnt = 0;
        for(int i =0;i < n;i++)
            cin>>A[i];
        MergeSort(A,0,n-1);
        cnt -= k;
        if(cnt<0) cnt=0;
        cout<

参考文章:

https://blog.csdn.net/crescent__moon/article/details/38424829

https://www.cnblogs.com/neopenx/p/4465348.html

https://blog.csdn.net/acm_jl/article/details/51147010

https://blog.csdn.net/qq_39627843/article/details/81204671

归并排序算法参考

你可能感兴趣的:(HDU 4911 逆序数对)