您的位置:首页 > 产品设计 > UI/UE

POJ 2299 Ultra-QuickSort (树状数组求逆序数+离散化)

2014-07-31 23:40 501 查看
Ultra-QuickSort
Time Limit:7000MS Memory Limit:65536KB 64bit
IO Format:
%I64d & %I64u
Submit Status Practice POJ
2299

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


题意:给你一组数,规定只能交换相邻的数,问最少交换多少次才能使这组数按升序排列。

心得:第一眼看题的时候,一想相邻元素之间交换,果断脑残地想到了冒泡啊,多聪明。。。一交TLE,这不是必须超嘛,要是不超,就是出题的二货了。后来,才知道这题也是求逆序数的,因为有多少逆序数,就必须交换多少次才能变成有序的。

分析:又是求逆序数问题。果断再次树状数组搞一下,如果数据不是很大, 可以一个个插入到树状数组中, 每插入一个数, 统计比他小的数的个数,对应的逆序为 i- getsum( data[i] ),其中 i 为当前已经插入的数的个数,sum(data[i])为比 data[i] 小的数的个数i- sum(data[i]) 即比 data[i] 大的个数, 即逆序的个数。

但是本题的问题是给的数据太大,如果数据比较大,就必须采用离散化方法。直接加入树状数组是搞不定的,今天又学了个好东西——离散化。

离散化:

一关键字的离散化方法:

所谓离散化也就是建立一个一对一的映射。 因为求逆序时只须要求数据的相应大小关系不变。

如: 10 30 20 40 50 与 1 3 2 4 5 的逆序数是相同的

定义一个结构体 struct Node{ int data; // 对应数据

int pos; // 数据的输入顺序 };

先对 data 升序排序, 排序后,pos 值对应于排序前 data 在数组中的位置。再定义一个数组 p
, 这个数组为原数组的映射。以下语句将按大小关系

把原数组与 1到 N 建立一一映射。

int id= 1; p[ d[1].pos ]= 1;
for( int i= 2; i<= n; ++i )
if( d[i].data== d[i-1].data ) p[ d[i].pos ]= id;
else   p[ d[i].pos ]= ++id;


二关健字的离散方法:

先对第一个关键字进行离散化,然后对第二关键字排序。

本题是一个关键字的离散化。

AC代码:

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <vector>
#include <queue>
#include <set>
#include <map>
#include <string>
#include <cmath>
#include <cstdlib>
#include <ctime>
#define INF 0x7fffffff
#define LL long long
using namespace std;
const int maxn = 500000 + 10;
const int maxx = 999999999;
LL c[maxn];

struct node
{
    int pos,val;
};
node a[maxn];
int reflect[maxn];
int n;

bool cmp(const node a,const node b)
{
    return a.val<b.val;
}

int lowbit(int x)
{
    return x & (-x);
}

void add(int x,int d)
{
    while(x <= n)
    {
        c[x] += d;
        x += lowbit(x);
    }
}

int sum(int x)
{
    int ret = 0;
    while(x > 0)
    {
        ret += c[x];
        x -= lowbit(x);
    }
    return ret;
}

int main()
{
    while(scanf("%d",&n)!=EOF && n)
    {
        LL cnt = 0;
        for(int i=1; i<=n; i++)
        {
            scanf("%d",&a[i].val);
            a[i].pos = i;
        }
        sort(a+1,a+1+n,cmp);
        for(int i=1; i<=n; i++)  c[i]=0;
        for(int i=1; i<=n; i++)  reflect[a[i].pos] = i;          //离散化
        for(int i=1; i<=n; i++)
        {
            add(reflect[i],1);
            cnt += i - sum(reflect[i]);          //求逆序数
        }
        printf("%lld\n",cnt);
    }
    return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: