题目链接:POJ 2299 Ultra-QuickSort

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

大概题意:

给一串长度为N的序列, 问要经过多少次调换才能形成一个升序。

大致思路:

记录元素的大小及坐标, 因为要形成升序,前面大的元素要交换到后面,所以就是转换成了求一段序列的逆序数。

这道题很容易想到冒泡排序法暴力,复杂度O(N^2) ,但这道题看到有求逆序数,优先用树状数组或线段树,如果用树状数组,效率会快很多很多。

之前写过一篇用树状数组求逆序数的方法,可以参考一下:链接

如果用树状数组,那么问题就来了,a[ i ] 的范围【0, 999999999】, 数据范围有点大,而且通过样例可以发现, 数据也不是连续的,所以按数据范围开数组这个太恶心了,我们不妨做一下离散化的处理,之后求逆序数就可以交给树状数组啦。

1、为什么要离散化?

正如前面所说的a[ i ]的范围最大可以去到 999,999,999. 如果给一组的一组数据是   999999999, 1, 0, 5,3 . 就5个数, 但树状数组的范围就要开到【0, 999999999】, 造成了大量的内存浪费,而且题目也不允许,这笔买卖可不划算。所以我们要用把这五个数离散化一下。

2、如何离散化?

记录他们的下标,用下标来搞事情。

既然要同时记录下标和数值,我们可以考虑两种映射

①map, 效率有点低

②定义一个结构体 v用于记录数值(排序用), no用于记录下标。

struct node

{

  int v, no;

};

输入序列之后,按升序排序,这样我们就可以得到一个按元素升序排好序表示每个元素出现的顺序的序列。

举个栗子: 999999999, 1, 0, 5, 3

t.v 9...9 1 0 5 3
t.no 1 2 3 4 5
t.v (排序后) 0 1 3 5 9...9
t.no(排序后) 3 2 5 4 1

很显然,我们就可以发现排序后的 t.no 序列的逆序数就是需要交换的次数,例如9...9 的 下表为 1 ,它前面 t.no 比它大的数有四个, 说明在原序列(未排序)中 9...9的后面(因为下标也代表出现的顺序,越大越靠后)比 9...9 小(因为排序)的有四个。所以问题到这里就可以交给树状数组去解决啦!

AC code:

///poj 2299 树状数组 离散化 求逆序数
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define INF ox3f3f3f3f
using namespace std; const int MAXN = ; struct node
{
int v;
int no;
}c[MAXN]; int aa[MAXN];
int N; bool cmp(struct node a, struct node b)
{
return a.v < b.v;
} int lowbit(int i)
{
return i&(-i);
} void add(int i, int value)
{
while(i <= N)
{
aa[i]+=value;
i+=lowbit(i);
}
} int sum(int i)
{
int res = ;
while(i > )
{
res+=aa[i];
i-=lowbit(i);
}
return res;
} int main()
{
while(scanf("%d", &N) != EOF && N)
{
memset(aa, , sizeof(aa));
for(int i = ; i <= N; i++)
{
scanf("%d", &c[i].v);
c[i].no = i;
}
sort(c+, c+N+, cmp);
long long int ans = ;
for(int i = ; i <= N; i++)
{
add(c[i].no, );
ans += i-sum(c[i].no);
}
printf("%lld\n", ans);
}
return ;
}

当然如果这样子你还是没办法理解,那我们可以在进一步转换,通过数组 aa[ ], 还原回原本的序列结合冒泡排序的思想进行求解

t.v 0 1 3 5 9...9
t.no 3 2 5 4 1
aa[ t.no ] 1 2 3 4 5
aa[1] ~ aa[5] 5 2 1 4 3

很明显,这里的aa[ i ] 表示的是第 i 个元素在整个序列中是第 aa[ i ] 大。那么结合冒泡排序的思想,这里的逆序数表示的是当前元素转到指定位置需要swap多少次

i t[1] t[2] t[3] t[4] t[5] i - sum( aa[i] )
1 0 0 0 0 1 1 - 1 = 0
2 1 0 0 1 2 - 1 = 1
3 1 1 0 0 1 3 - 1 = 2
4 1 1 0 1 1 4 - 3 = 1
5 1 1 1 1 1 5 - 3 = 2

例如, 按照冒泡排序法,第二个数 1 要跟 9... 9 交换位置一次, 序列变成 1,9...9, 0, 5, 3

第三个数 0 要跟前面两个数交换位置,所以交换次数为2, 序列变成 0,1,9...9,5,3

第四个数 5 要跟前面的 9...9 交换位置,交换次数为1,序列 0,1,5,9...9, 3

第五个数 3 要跟前面两个数交换位置,交换位置为2, 序列 0,1,3,5,9...9

答案就是:0+1+2+1+2 = 6;

AC code:

///poj 2299 树状数组 离散化 求逆序数
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define INF ox3f3f3f3f
using namespace std; const int MAXN = ; struct node
{
int v;
int no;
}c[MAXN]; int aa[MAXN];
int t[MAXN];
int N; bool cmp(struct node a, struct node b)
{
return a.v < b.v;
} int lowbit(int i)
{
return i&(-i);
} void add(int i, int value)
{
while(i <= N)
{
t[i]+=value;
i+=lowbit(i);
}
} int sum(int i)
{
int res = ;
while(i > )
{
res+=t[i];
i-=lowbit(i);
}
return res;
} int main()
{
while(scanf("%d", &N) != EOF && N)
{
memset(t, , sizeof(t));
for(int i = ; i <= N; i++)
{
scanf("%d", &c[i].v);
c[i].no = i;
}
sort(c+, c+N+, cmp); for(int i = ; i <= N; i++)
aa[c[i].no] = i; long long int ans = ;
for(int i = ; i <= N; i++)
{
add(aa[i], );
ans += i-sum(aa[i]);
}
printf("%lld\n", ans);
}
return ;
}

POJ 2299 【树状数组 离散化】的更多相关文章

  1. POJ 2299 树状数组+离散化求逆序对

    给出一个序列 相邻的两个数可以进行交换 问最少交换多少次可以让他变成递增序列 每个数都是独一无二的 其实就是问冒泡往后 最多多少次 但是按普通冒泡记录次数一定会超时 冒泡记录次数的本质是每个数的逆序数 ...

  2. Ultra-QuickSort (POJ 2299)树状数组+离散化

    题目链接 Description In this problem, you have to analyze a particular sorting algorithm. The algorithm ...

  3. poj 2299 树状数组求逆序数+离散化

    http://poj.org/problem?id=2299 最初做离散化的时候没太确定可是写完发现对的---由于后缀数组学的时候,,这样的思维习惯了吧 1.初始化as[i]=i:对as数组依照num ...

  4. poj 2299 树状数组求逆序对数+离散化

    Ultra-QuickSort Time Limit: 7000MS   Memory Limit: 65536K Total Submissions: 54883   Accepted: 20184 ...

  5. POJ 2299树状数组求逆序对

    求逆序对最常用的方法就是树状数组了,确实,树状数组是非常优秀的一种算法.在做POJ2299时,接触到了这个算法,理解起来还是有一定难度的,那么下面我就总结一下思路: 首先:因为题目中a[i]可以到99 ...

  6. Ultra-QuickSort POJ - 2299 树状数组求逆序对

    In this problem, you have to analyze a particular sorting algorithm. The algorithm processes a seque ...

  7. hdu4605 树状数组+离散化+dfs

    Magic Ball Game Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others) ...

  8. BZOJ_5055_膜法师_树状数组+离散化

    BZOJ_5055_膜法师_树状数组+离散化 Description 在经历过1e9次大型战争后的宇宙中现在还剩下n个完美维度, 现在来自多元宇宙的膜法师,想偷取其中的三个维度为伟大的长者续秒, 显然 ...

  9. POJ 2299 Ultra-QuickSort(树状数组+离散化)

    http://poj.org/problem?id=2299 题意:给出一组数,求逆序对. 思路: 这道题可以用树状数组解决,但是在此之前,需要对数据进行一下预处理. 这道题目的数据可以大到999,9 ...

随机推荐

  1. oracle 单实例DG(闪回技术四)

    一,flashback Oracle Flashback技术是一组数据库特性,它可以让你查看数据库对象的过去状态,或者将数据库对象返回到以前的状态,而无需使用基于时间点的介质恢复.根据数据库的变化,闪 ...

  2. TOJ 2926 Series

    Description An arithmetic series consists of a sequence of terms such that each term minus its immed ...

  3. 数组和json的相互转换

    json_encode() <?php /*****一维数组*********/ //有键 $arr = array( 'a'=>1, 'b'=>2, 'c'=>3, ); $ ...

  4. 内行看门道:看似“佛系”的《QQ炫舞手游》,背后的音频技术一点都不简单

    欢迎大家前往腾讯云+社区,获取更多腾讯海量技术实践干货哦~ 本文由腾讯游戏云发表于云+社区专栏 3月14日,腾讯旗下知名手游<QQ炫舞>正式上线各大应用商店,并迅速登上App Store免 ...

  5. 读《Wireshark网络分析就这么简单》读书笔记

    晚上花了两个多小时看完这本书,记录下一些看书过程中的笔记. 一.问题:A和B 是否能正常通信? 两台服务器A和服务器B的网络配置 A                                  ...

  6. JUnit测试框架的使用

    1.学习Junit框架的使用 可通过以下两个示例进行学习. A.Junit使用方法示例1 1)把Junit引入当前项目库中 新建一个 Java 工程—coolJUnit,打开项目coolJUnit 的 ...

  7. python数据处理

    1.数据清洗 1.1 数据格式化 数据格式化是数据清洗常见的形式之一,就是将可读性差的或无法阅读的数据转换成可读性较强的数据格式. python对字符串和数字都有格式化的方法,如%s, %d分别代表格 ...

  8. git获取别人远程dev分支上的代码

    我们在使用 git clone  xxx.git 下载代码的时候,获取到的只是 master上的代码 假入有个 dev 分支我们想获取上面的代码怎么办! #下载dev分支上的代码并切换到dev分支 g ...

  9. Protocol Buffers序列化原理

    1. 使用Varint编码数据,越小的数据,用少的字节编码.如小于128的用一个字节编码,大于128的用多个字节编码.同时,每个字节最高为1或者0表示是否为数字的一部分. 2. 由于负数的补码表示很大 ...

  10. 【代码笔记】Java基础:Java的方法和类

    面向过程与面向对象都是我们编程中,编写程序的一种思维方式.例如:公司打扫卫生(擦玻璃.扫地.拖地.倒垃圾等), 按照面向过程的程序设计方式会思考“打扫卫生我该怎么做,然后一件件的完成”,最后把公司卫生 ...