POJ :http://poj.org/problem?id=2299
题意:给一个大小为n的数组,乱序! 对数组进行排序,排序的办法是,每次只能交换相邻的两个数字。(也就是冒泡排序)
询问,最少的冒泡排序的交换次数为多少。
n大小为500000,每个数字的大小为999999999(9个9,int范围内)。
很惭愧,这么简单的题目,一开始我没有什么清晰的思路。。。太蠢了。
后来明白是要求这个数组的“逆序数”,即为答案。
逆序数:所有在数组中的数,分别求 其在数组中,它后面比它小的数字的个数 加起来。
比如样例 9 1 0 5 4 ,那么9后面有4个比它小的,1有一个,0没有,5有1个,4没有。所以逆序数就是 4+1+1=6,即为答案,而这也是9要冒泡交换的次数。
以上都是线代的内容,惭愧,线代一直睡觉,擦线过的。。。。
用树状数组如何求逆序数?
其实就是求,对于数组里每一个数,在后面的 比它小的 数字的个数。
这也是树状数组的一个很常用的功能。 将数的大小开一个数组,从数组的一边,边扫,边更新树状数组,这时树状数组记录的是“从数组前到这个位置,各个数出现的个数”,同时边进行查询操作。
然而,数的大小此题为999999999,这么大的数组是开不起来的,但是n最大为500000,所以很自然就想到了,对题目给的数字进行从1到n的一个映射(离散化),所以想到映射就能写出此题了。
映射的步骤很偷懒的就想到了用map。今日也是涨了姿势,都说map慢,一直没有见证。理论复杂度nlogn的map,交上去以后TE了。怀疑人生,以为是哪个地方写错了,多交了几发,都是TE。。。
于是,改用普通排序,自己手动映射,就过了。。。
以后能不用map就不用map吧。。
还有POJ是不咨瓷 <bits/stdc++>的。。
上一下代码。。
#include "stdio.h"
#include "map"
#include "cstring"
#include "algorithm"
using namespace std;
#define inf 500009
#define INF 999999999
#define ll long long
#define loop(x,y,z) for(x=y;x<z;x++)
int n;
int c[inf]; //树状数组,实际大小为映射的大小N
int m[inf];//映射原始下标到排序后下标
ll ans;
struct node
{
int x,id;
bool operator< (const node& j)const
{
return x<j.x;
}
}num[inf]; //题数据以及下标
void init()
{
memset(c,0,sizeof c);
ans=0;
sort(num+1,num+n+1);
int i;
loop(i,1,n+1)
m[num[i].id]=i;
}
int lowbit(int i)
{
return i&-i;
}
void add(int i)
{
while(i<=n)
{
c[i]++;
i+=lowbit(i);
}
}
ll query(int i)
{
ll sum=0;
while(i>0)
{
sum+=(ll)c[i];
i-=lowbit(i);
}
return sum;
}
int main()
{
int i,j;
while(scanf("%d",&n)&&n)
{
loop(i,1,n+1){scanf("%d",&num[i].x);num[i].id=i;}
init();
for(i=n;i>0;i--)
{
ans+=query(m[i]-1);
add(m[i]);
}
printf("%lld\n",ans);
}
return 0;
}
本文解析了POJ 2299题目的解题思路,介绍了如何利用树状数组求解数组的逆序数,进而得出冒泡排序所需的最小交换次数。文章还分享了使用映射技巧解决大数据范围问题的经验。
6871

被折叠的 条评论
为什么被折叠?



