[noj 1395] 乾坤大挪移 (树状数组,归并排序)

题目链接: http://acm.nbut.cn/Problem/view.xhtml?id=1395
这道题是让你求相邻两个数进行多少次交换后使得增序,其实并不复杂,就是让你求逆序数。
所谓逆序数,就是指一个序列C[i],统计处于序列的每个数的比这个数大并且排在它前面的数的数目,然后对于所有数,把这个数目加起来求和就是了。
比如说:1 4 2 5 3
1前面有0个
4前面有0个
2前面有1个
5前面有0个
3前面有2个
所以逆序数为: 0+0+1+0+2 = 3.
但是单纯的暴力是不解决问题的,因为数据范围是 2<=n<=1200,时间复杂度为O(n^2),暴力肯定tle。
因此可以用树状数组来做。时间复杂度为O(nlogn)。
树状数组可以用三个函数来解决:

int lowbit(int x)

{

return x & -x; //x和-x求与,返回的结果表示每一层的结点,并且越大越靠近根。

}

int getsum(int x)

{

int ret = 0;

while(x > 0)

{

ret += c[x];

x -= lowbit(x);

}

return ret;

}

void update(int x)

{

while(x <= n)

{

c[x] += 1;//这里加1表示有一个数放到了树状数组中。

x += lowbit(x);

}

}

但是如何用树状数组来解决逆序数问题呢?
这里我们用到了上面代码中getsum的操作,其用意是来获取比当前数小的数,但是我们要得到的是比当前数大的数,怎么办呢?可以这样做 :用i - getsum(x)来表示比当前数大的数,然后每次getsum操作之后进行统计即可。
为了更加直观一点,我们还是用上面的例子:1 4 2 5 3(i从0开始)
i = 0,插入了1,getsum(1)返回0,update (1) sum =  i - 0 = 0;
i = 1,插入了4,getsum(4)返回1,update(4) sum =  i - 1 = 0; 
i = 2,插入了2,getsum(2)返回1,update(2) sum =  i - 1 = 1;
i = 3,插入了5,getsum(5)返回3,update(5) sum =  i - 3 = 0;
i = 4,插入了3,getsum(3)返回2,update(3) sum =  i - 2 = 2;
所以sum = 0 + 0 + 1 + 0 + 2 = 3; 
ok,问题解决。
代码如下:

#include<stdio.h>
#include<string.h>
int c[1203];
int n;
int lowbit(int x)
{
return x & -x; //x和-x求与,返回的结果表示每一层的结点,并且越大越靠近根。
}
int getsum(int x)
{
int ret = 0;
while(x > 0)
{
ret += c[x];
x -= lowbit(x);
}
return ret;
}
void update(int x)
{
while(x <= n)
{
c[x] += 1;//这里加1表示有一个数放到了树状数组中。
x += lowbit(x);
}
}
int main()
{
while(~scanf("%d", &n))
{
memset(c, 0, sizeof(c));
int sum = 0;
for(int i = 0; i < n; i++)
{
int x;
scanf("%d", &x);
sum += i - getsum(x);
update(x);
}
printf("%d\n", sum);
}
return 0;
}

当然还有一种写法,就是用归并排序。
直接贴代码,今天累了,不解释了~:

#include<stdio.h>
#include<string.h>
#include<algorithm>

using namespace std;

int a[1204];
int b[1204];
int cnt;

void merge_sort(int x, int y)
{
if(y - x > 1)
{
int m = x + (y - x) / 2;
int p = x, q = m, i = x;
merge_sort(x,m);
merge_sort(m,y);
while(p < m || q < y)
{
if(q >= y || (p < m && a[p] <= a[q]))
{
b[i++] = a[p++];
}
else
{
b[i++] = a[q++];
cnt += m - p;
}
}
for(i = x; i < y; i++)
{
a[i] = b[i];
}
}
}
int main()
{
int n;
while(~scanf("%d",&n))
{
memset(b, 0, sizeof(b));
cnt = 0;
for(int i = 0; i < n; i++)
{
scanf("%d", &a[i]);
}
merge_sort(0, n);
printf("%d\n", cnt);
}

return 0;
}


黑赵晓,我们特专业!

你可能感兴趣的:([noj 1395] 乾坤大挪移 (树状数组,归并排序))