Ultra-QuickSort
题目链接:http://poj.org/problem?id=2299
题目大意:
有一串序列,(其中数字各不相同),每次只能够交换相邻的两个数字,问将其排为升序所需的交换次数。
解题思路:
这道题其实就是求逆序对的数目。可以看下面的博客,讲解了问什么这样的交换次数就是逆序对的数目。(大致就是冒泡排序的思想)
http://blog.csdn.net/ojshilu/article/details/17066737
至于求逆序数,如果用朴素的方法,为O(N^2)。不可取。
还有三种 O(NlogN)的方法:
(1) 通过归并排序求逆序对
将一个序列分为左右两端,那么通过遍历两端子序列,可以求出左右两端的逆序对数目。通过分治的方法再将左边和右边分为两端,知道 L=R结束。
归并排序
const int maxn = 555555;
int n, a[maxn], b[maxn];
typedef long long ll;
ll mergesort(int l, int r) {
if (l == r) return 0;
ll cnt = 0;
int m = (l + r) >> 1;
cnt = mergesort(l, m) + mergesort(m + 1, r);
int i = l, j = m + 1, k = l;
while(i <= m && j <= r) {
if (a[i] <= a[j]) b[k++] = a[i++];
else {
b[k++] = a[j++];
cnt += (m - i + 1);
}
}
while(i <= m) b[k++] = a[i++];
while(j <= r) b[k++] = a[j++];
for (i = l; i <= r; i++) a[i] = b[i];
return cnt;
}
int main () {
while(~scanf("%d", &n) && n) {
for (int i = 0; i < n; i++) scanf("%d", a + i);
printf("%I64d\n", mergesort(0, n - 1));
}
return 0;
}
(2)用树状数组求逆序对(必须要离散化,所以比上一种方法略慢)
//树状数组
const int maxn = 555555;
typedef long long ll;
int n;
ll a[maxn], pos[maxn];
struct node {
int v, i;
}e[maxn];
bool cmp(node s, node v){
return s.v < v.v;
}
inline int lowbit(int x) {
return (x & -x);
}
void update(int x, int v) {
for (int i = x; i <= n; i += lowbit(i)) a[i] += v;
}
ll query(int x) {
ll ret = 0;
for (int i = x; i; i -= lowbit(i)) ret += a[i];
return ret;
}
int main () {
while(scanf("%d", &n) && n) {
for (int i = 1; i <= n; i++) {
a[i] = 0;
scanf("%d", &e[i].v);
e[i].i = i;
}
//离散化
sort(e + 1, e + n + 1, cmp);
for (int i = 1; i <= n; i++) pos[i] = e[i].i;
int x;
ll ret = 0;
for (int i = 1; i <= n; i++) {
ret += query(pos[i]);
update(pos[i], 1);
}
printf("%I64d\n", (ll)n * (n - 1) / 2 - ret);
}
return 0;
}
(3)用线段树求逆序对(和第二种一样)