POJ 2299 Ultra-QuickSort 线段树
2016-04-21 17:23
459 查看
Ultra-QuickSort
![]() 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 Source Waterloo local 2005.02.05 |
将一组各不相同的数升序排列,每次只能交换相邻的两个数,求最小的交换次数。
这里用线段树解决,参考博客:点击打开博客链接
我们先将原数组每个值附上一个序号index,再将它排序。如题目的例子:
num: 9 1 0 5 4
index: 1 2 3 4 5
排序后:
num: 0 1 4 5 9
index: 3 2 5 4 1
由于排序后num为0的点排在原来数组的第3个,为了将它排到第一个去,至少需要向前移动两次,它也等价于最小的数0之前有2个数比它大(所以要移动两次),将0移到它自己的位置后,我们将0删掉(目的是为了不对后面产生影响)。再看第二大的数1,它出现在原数组的第二个,他之前有一个数比它大所以需要移动一次。这样一直循环下去那么着5个数所需要移动的次数就是:
num: 0 1 4 5 9
次数 2 1 2 1 0
将次数全部要加起来就是最后所需要移动的总次数。
在建一棵树时,不是直接将原来的num放进树里面,而是将它的下标放进树里面,最初每个节点上赋值为1.然后当查找第一个num时,由于是找的下标为3的位置,所以我们就直接找区间[1,3)之间有多少个1(就是求前导和),这里面1的个数就是第一个num=0所要移动的次数,然后我们把0去掉,其实也就是把下标为3的那个1去掉。这样每个值就依次计算出来了。
当然其实只要是想明白了,不用线段树,直接用树状数组写起来会简便很多。(因为每次只需要计算前导和以及去掉某一个点,是对点的操作)。
代码:
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int MAXN = 500000 + 10;
int total[MAXN << 2], num[MAXN], index[MAXN];
int N;
int ql, qr;
//ql,qr代表查询区间的左右端点
//查询 [ql , qr] 前导和,o是当前结点编号,L和R是当前结点的左右端点
int query(int o, int L, int R)
{
int M = L + (R - L) / 2, ans = 0;
if (ql <= L && qr >= R) return total[o]; //当前结点完全包含在查询区间内
if (ql <= M) ans += query(2 * o, L, M); //往左走,查询左子树
if (M < qr) ans += query(2 * o + 1, M + 1, R); //往右走,查询右子树
return ans;
}
int p, v; //代表修改点的位置和要修改的数值
void update(int o, int L, int R)
{
total[o] += v; //更新树
if (L == R) return;
int M = L + (R - L) / 2;
if (p <= M) update(2 * o, L, M); //递归更新左子树或者右子树
else update(2 * o + 1, M + 1, R);
}
//给下标排序的函数
bool cmp(int x, int y)
{
return num[x] < num[y];
}
int main()
{
while (~scanf("%d", &N), N)
{
memset(total, 0, sizeof(total));
for (int i = 1; i <= N; i++)
{
scanf("%d", &num[i]);
p = i; //初始化每个结点赋值1,并更新树
v = 1;
update(1, 1, N);
index[i] = i; //记录下标
}
sort(index + 1, index + N + 1, cmp); //将下标按num的大小排序
long long ans = 0;
for (int i = 1; i <= N; i++)
{
ql = 1; //查询区间[ql, qr] <=> [1, index[i]]
qr = index[i];
ans += (query(1, 1, N) - 1); //当前位置不算交换一次,减一
p = index[i]; //查询后就将这个数去掉
v = -1;
update(1, 1, N);
}
printf("%lld\n", ans);
}
return 0;
}
树状数组解决:点击打开博客链接树状数组解决
相关文章推荐
- C#数据结构之顺序表(SeqList)实例详解
- Lua教程(七):数据结构详解
- 解析从源码分析常见的基于Array的数据结构动态扩容机制的详解
- C#数据结构之队列(Quene)实例详解
- C#数据结构揭秘一
- C#数据结构之单链表(LinkList)实例详解
- 数据结构之Treap详解
- 用C语言举例讲解数据结构中的算法复杂度结与顺序表
- C#数据结构之堆栈(Stack)实例详解
- C#数据结构之双向链表(DbLinkList)实例详解
- JavaScript数据结构和算法之图和图算法
- Java数据结构及算法实例:冒泡排序 Bubble Sort
- Java数据结构及算法实例:插入排序 Insertion Sort
- Java数据结构及算法实例:考拉兹猜想 Collatz Conjecture
- java数据结构之java实现栈
- java数据结构之实现双向链表的示例
- Java数据结构及算法实例:选择排序 Selection Sort
- Java数据结构及算法实例:朴素字符匹配 Brute Force
- Java数据结构及算法实例:汉诺塔问题 Hanoi
- Java数据结构及算法实例:快速计算二进制数中1的个数(Fast Bit Counting)