逆序数 poj2299 Ultra-QuickSort

本文介绍了一种名为Ultra-QuickSort的排序算法,并详细分析了如何通过该算法确定排序过程中所需的交换次数。利用归并排序在合并过程中计算逆序数的方法,实现了对输入序列的高效处理。

这里写图片描述
Ultra-QuickSort
Time Limit: 7000MS Memory Limit: 65536K
Total Submissions: 59643 Accepted: 22101
Description

In this problem, you have to analyze a particular sorting algorithm. The algorithm processes a sequence of n distinct integers by swapping two adjacent sequence elements until the sequence is sorted in ascending order. For the input sequence
9 1 0 5 4 ,

Ultra-QuickSort produces the output
0 1 4 5 9 .

Your task is to determine how many swap operations Ultra-QuickSort needs to perform in order to sort a given input sequence.
Input

The input contains several test cases. Every test case begins with a line that contains a single integer n < 500,000 – the length of the input sequence. Each of the the following n lines contains a single integer 0 ≤ a[i] ≤ 999,999,999, the i-th input sequence element. Input is terminated by a sequence of length n = 0. This sequence must not be processed.
Output

For every input sequence, your program prints a single line containing an integer number op, the minimum number of swap operations necessary to sort the given input sequence.
Sample Input

5
9
1
0
5
4
3
1
2
3
0
Sample Output

6
0
题意:这个题的意思是问你冒泡排序中交换值的次数,也就是让你求序列中的逆数对之和;

求在数列的逆序数可以使用归并排序。归并排序是将数列a[l,h]分成两半a[l,mid]和a[mid+1,h]分别进行归并排序,然后再将这两半合并起来。在合并的过程中(设l<=i<=mid,mid+1<=j<=h),当a[i]<=a[j]时,并不产生逆序数;当a[i]>a[j]时,在前半部分中比a[i]大的数都比a[j]大,将a[j]放在a[i]前面的话,逆序数要加上mid+1-i。因此,可以在归并排序中的合并过程中计算逆序数

这个代码好像只能在pku上ac;那就粘贴第二份代码,这是重做之后发现的错误

#include<stdio.h>
#include<string.h>
#include<iostream>
#include<algorithm>
using namespace std;
int n;
int A[500005],T[500005];
__int64 ans;
void mersort(int x,int y)
{
    if(y-x<=1)
        return;
    int mid=(x+y)/2;
    mersort(x,mid);
    mersort(mid,y);
    int p=x,i=x,q=mid;
    while(p<mid||q<y)
    {
        if(q==y||(p<mid&&A[p]<=A[q]))
            T[i++]=A[p++];
        else if(p==mid||(A[q]<A[p]))
        {
            if(p<mid) ans+=(mid-p);
            T[i++]=A[q++];
        }
    }
    for(int i=x; i<y; i++)
    {
        A[i]=T[i];
    }
}
int main()
{
    while(~scanf("%d",&n)&&n)
    {
        memset(A,0,sizeof(A));
        memset(T,0,sizeof(T));
        for(int i=0; i<n; i++)
        {
            scanf("%d",&A[i]);
        }
        ans=0;
        mersort(0,n);
        printf("%I64d\n",ans);
    }
    return 0;
}
#include<stdio.h>
#include<string.h>
#include<iostream>
#include<algorithm>
using namespace std;
#define LL long long int
int a[500005],b[500005];
int n;
LL ans;//极限情况500000*500000会爆,所以开LL
void mersort(int x,int y)
{
    if(y-x<=1)
        return ;
    int mid=(x+y)/2;
    mersort(x,mid);
    mersort(mid,y);
    int p=x,i=x,q=mid;
    while(p<mid||q<y)
    {
        if(q==y||(p<mid&&a[p]<=a[q]))//注意,不是a[p]<=a[mid],弄清比较的对象
        //x到y排序,所以应该和a[q]比,从而把后面的一到前面去。
            b[i++]=a[p++];
        else if(p==mid||a[p]>a[q])
        {
            if(p<mid) ans+=(mid-p);
            b[i++]=a[q++];
        }
    }
    for(int i=x; i<y; i++)
        a[i]=b[i];
}
int main()
{
    while(~scanf("%d",&n)&&n)
    {
        memset(a,0,sizeof(a));
        memset(b,0,sizeof(b));
        for(int i=0; i<n; i++)
            scanf("%d",&a[i]);
        ans=0;
        mersort(0,n);
        printf("%lld\n",ans);
    }
    return 0;
}

转载于:https://www.cnblogs.com/zxy160/p/7215140.html

<think>好的,我需要解决用户关于Ultra-QuickSort算法的问题,包括如何实现和解释它。首先,我得先理解这个算法的本质。根据用户提供的引用,Ultra-QuickSort实际上是通过交换相邻元素来排序,而问题是要计算需要多少次这样的交换操作。听起来这类似于计算逆序对的数量,因为每次交换相邻元素只能消除一个逆序对。 接下来,我需要回忆一下相关的算法。通常,计算逆序对的高效方法有归并排序和树状数组。用户提到的引用[3]中提到了归并排序,这可能是一个常用的方法。归并排序在分治的过程中可以统计逆序对的数量,具体来说,在合并两个有序子数组时,如果左边的元素大于右边的元素,那么左边的剩余元素都会与这个右边的元素形成逆序对,这样就能统计出数量。 那么,如何将归并排序应用于这个问题呢?首先,归并排序会将数组分成两半,递归地对左右两部分排序,并统计各自的逆序对数目。然后在合并过程中,当左边的元素大于右边的元素时,就需要计算左边剩余元素的数量,并将这个数量加到总的逆序对数目中。这样,总的逆序对数目就是整个数组需要交换的次数,也就是Ultra-QuickSort所需的swap操作次数。 接下来,我需要考虑具体的实现步骤。例如,在归并排序的合并阶段,如何正确统计逆序对的数量。假设有一个数组arr,分成左半部分和右半部分,分别排序后合并。当左半部分的当前元素大于右半部分的当前元素时,说明存在逆序对,此时逆序对的数量应该是左半部分剩余元素的数量,因为左半部分已经是有序的,所有剩余元素都会比当前右半部分的元素大。 另外,需要考虑递归的终止条件,即当数组长度为1时,逆序对数量为0。然后,在递归返回时,将左右子数组的逆序对数目加上合并过程中产生的数目,得到总的数目。 测试一下这个思路是否正确。例如,对于输入序列9 1 0 5 4,正确的输出是交换次数为6。那么用归并排序计算逆序对是否得到6?让我手动计算一下。原始序列是9,1,0,5,4。分解后的逆序对可能有:9与1、0、5、4;1与0;5与4。总共有6个逆序对,确实如此。因此,这个方法是正确的。 接下来,我需要编写代码实现这个算法。用Python的话,可以定义一个递归函数,返回排序后的数组和逆序对的数量。在合并过程中,使用双指针遍历左右两个子数组,比较元素大小,并按顺序合并到临时数组中。每当左指针的元素大于右指针的元素时,就将右指针的元素放入临时数组,并增加逆序对的数量,增加的数量为左子数组剩余元素的个数。 需要注意的是,归并排序的实现中,临时数组的处理可能会影响空间复杂度,但这里问题规模应该可以接受。此外,Python中处理大数组时需要注意递归深度,不过题目中的例子应该不会有问题。 最后,验证代码的正确性。用给出的测试用例9 1 0 5 4,应该输出6。再比如,对于已经排序的数组,逆序对数目为0。另一个例子,比如3,2,1,应该有3个逆序对,交换次数为3。 总结一下,Ultra-QuickSort的swap次数等于数组中的逆序对数目,而归并排序是一个有效的方法来计算这个数目。因此,实现的关键在于正确修改归并排序以统计逆序对的数量。</think>### Ultra-QuickSort算法解析与实现 Ultra-QuickSort的核心是**通过交换相邻元素消除逆序对**,其最小交换次数等于数组中逆序对的总数。例如输入序列$9\ 1\ 0\ 5\ 4$需要6次交换[^1][^3]。 #### 算法原理 1. **逆序对定义**:若$i < j$且$a_i > a_j$,则$(a_i,a_j)$构成逆序对 2. **等价关系**:每次相邻交换最多消除一个逆序对 3. **最优性证明**:最小交换次数=逆序对总数 #### 实现方法(归并排序) ```python def count_inversions(arr): if len(arr) <= 1: return arr, 0 mid = len(arr) // 2 left, inv_left = count_inversions(arr[:mid]) right, inv_right = count_inversions(arr[mid:]) merged = [] inv_count = inv_left + inv_right i = j = 0 while i < len(left) and j < len(right): if left[i] <= right[j]: merged.append(left[i]) i += 1 else: merged.append(right[j]) j += 1 inv_count += len(left) - i merged += left[i:] merged += right[j:] return merged, inv_count # 示例用法 arr = [9, 1, 0, 5, 4] _, swaps = count_inversions(arr) print(f"需要最少交换次数:{swaps}") # 输出6 ``` #### 代码解析 1. **递归分治**:将数组不断二分至单个元素 2. **合并统计**:合并有序子数组时计算跨区间的逆序对 3. **时间复杂度**:$O(n \log n)$,空间复杂度$O(n)$
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值