题目大意:给出一系列数字,交换相邻数字,得到升序排列,问交换操作的最小次数
输入:(可以有很多个case,输入以0结束)
第i个case的数字个数n(n < 500,000)
n个数字(各占一行)(0 ≤ a[i] ≤ 999,999,999)
0
输出:交换操作的最小次数
分析:这种交换操作首先想到冒泡,但500000用冒泡时间复杂度肯定超时了,所以想别的算法。此题就是要求逆序对的个数,用归并排序来记录,排序时一个数向前移动了多少位就是有多少个逆序对。
还有一种方法是树状数组+离散化。
由于数据最大999999999,而最多只有500000个数,所以没必要也不能开那么大的空间,因此进行离散化,离散化用一个结构体,val存储原数值,pos是原下标,然后按val对结构体从小到大排序。
for(i=1;i<=N;i++)a[p[i].pos]=i; 就使a数组存储了原来所有的大小信息;
比如 9 1 0 5 4 ------- 离散后的a数组
就是 5 2 1 4 3;从头到尾读入a数组中的元素,既可以保持原来的大小关系,还可以节省很多空间。
接下来说一下树状数组,每读入一个a数组中的数就更新树状数组,getsum就是得出在它前面出现过的比他小的数值的个数,然后i-getsum也就是当前位置减去比他小的就是逆序对的个数了。用树状数组能很方便的记录之前出现的、比他小的数值的个数。对比暴力枚举的复杂度n,树状数组将查询修改的复杂度降到了logn。
代码:
归并排序转载自http://blog.csdn.net/alongela/article/details/8119209
#include <iostream>
#include <cstdio>
using namespace std;
long long num[500005];
long long temp[500005];
int n;
long long ans;
void merge(int low, int mid, int high)
{
int i = low, j = mid + 1, k = low;
while (i <= mid && j <= high)
{
if (num[i] <= num[j])
{
temp[k++] = num[i++];
}
else
{
ans += j - k ;//统计逆序
temp[k++] = num[j++];
}
}
while (i <= mid) temp[k++] = num[i++];
while (j <= high) temp[k++] = num[j++];
for (i = low; i <= high; ++i)
{
num[i] = temp[i];
}
}
void mergeSort(int a, int b)
{
if (a < b)
{
int mid = (a + b) / 2;
mergeSort(a, mid);
mergeSort(mid + 1, b);
merge(a, mid, b);
}
}
int main()
{
while (scanf("%d", &n) != EOF && n > 0)
{
ans = 0;
for (int i = 0; i < n; ++i)
{
scanf("%lld", num + i);
}
mergeSort(0, n - 1);
printf("%lld\n", ans);
}
return 0;
}
树状数组+离散化转载自http://blog.csdn.net/alongela/article/details/8142965
#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
using namespace std;
const int N = 500005;
struct Node
{
int val;
int pos;
};
Node node[N];
int c[N], reflect[N], n;
bool cmp(const Node& a, const Node& b)
{
return a.val < b.val;
}
void update(int i) //往下更新
{
while(i > 0){
c[i]++;
i -= (i & (-i));
}
}
int getsum(int i){ //计算比i大的数的个数总和(包括i自己)
int total = 0;
while(i <= n){
total += c[i];
i += (i & (-i));
}
return total;
}
int main()
{
while (scanf("%d", &n) != EOF && n)
{
for (int i = 1; i <= n; ++i)
{
scanf("%d", &node[i].val);
node[i].pos = i;
}
sort(node + 1, node + n + 1, cmp); //排序
for (int i = 1; i <= n; ++i) reflect[node[i].pos] = i; //离散化
for (int i = 1; i <= n; ++i) c[i] = 0; //初始化树状数组
long long ans = 0;
for (int i = 1; i <= n; ++i)
{
ans += getsum(reflect[i]);
update(reflect[i]);
}
printf("%lld\n", ans);
}
return 0;
}