博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
洛谷 P1908 逆序对 Label:归并排序||树状数组 不懂
阅读量:6918 次
发布时间:2019-06-27

本文共 3915 字,大约阅读时间需要 13 分钟。

题目描述

猫猫TOM和小老鼠JERRY最近又较量上了,但是毕竟都是成年人,他们已经不喜欢再玩那种你追我赶的游戏,现在他们喜欢玩统计。最近,TOM老猫查阅到一个人类称之为“逆序对”的东西,这东西是这样定义的:对于给定的一段正整数序列,逆序对就是序列中ai>aj且i<j的有序对。知道这概念后,他们就比赛谁先算出给定的一段正整数序列中逆序对的数目。

输入输出格式

输入格式:

 

第一行,一个数n,表示序列中有n个数。

第二行n个数,表示给定的序列。

 

输出格式:

 

给定序列中逆序对的数目。

 

输入输出样例

输入样例#1:
65 4 2 6 3 1
输出样例#1:
11

说明

对于50%的数据,n≤2500

对于100%的数据,n≤40000。

代码:解法一

1 #include
2 #include
3 #include
4 #include
5 using namespace std; 6 int N,a[100005],temp[100005],cnt; 7 8 void print(){ 9 for(int i=1;i<=6;i++){10 printf("%d ",a[i]);11 }12 puts("");13 }14 15 void merge_sort(int l,int r){16 if(l>=r) return;17 int mid=(l+r)>>1;18 merge_sort(l,mid);merge_sort(mid+1,r);19 20 21 int i=l,j=mid+1,point=l;22 while(i<=mid&&j<=r){23 if(a[i]>a[j]){24 temp[point++]=a[j++];25 cnt+=(mid-i+1);26 }27 else{28 temp[point++]=a[i++];29 }30 }31 32 while(i<=mid) temp[point++]=a[i++];33 while(j<=r) temp[point++]=a[j++];34 35 for(int k=l;k<=r;++k)36 a[k]=temp[k];37 // print();38 }39 40 int main(){41 // freopen("01.txt","r",stdin);42 43 scanf("%d",&N);44 for(int i=1;i<=N;++i)45 scanf("%d",&a[i]);46 47 merge_sort(1,N);48 49 printf("%d\n",cnt);50 return 0;51 }

暴力枚举的话,可以过两个点~

转载题解如下:

归并排序是将数列a[l,h]分成两半a[l,mid]和a[mid+1,h]分别进行归并排序,然后再将这两半合并起来。

在合并的过程中(设l<=i<=mid,mid+1<=j<=h),当a[i]<=a[j]时,并不产生逆序数;当a[i]>a[j]时,在

前半部分中比a[i]大的数都比a[j]大,将a[j]放在a[i]前面的话,逆序数要加上mid+1-i。因此,可以在归并

排序中的合并过程中计算逆序数.

解法二:树状数组+离散化

1 #include 
2 #include
3 #include
4 #include
5 using namespace std; 6 7 const int N = 500005; 8 9 struct Node 10 { 11 int val; 12 int pos; 13 }; 14 15 Node node[N]; 16 int c[N], reflect[N], n; 17 18 bool cmp(const Node& a, const Node& b) 19 { 20 return a.val < b.val; 21 } 22 23 int lowbit(int x) 24 { 25 return x & (-x); 26 } 27 28 void update(int x) 29 { 30 while (x <= n) 31 { 32 c[x] += 1; 33 x += lowbit(x); 34 } 35 } 36 37 int getsum(int x) 38 { 39 int sum = 0; 40 while (x > 0) 41 { 42 sum += c[x]; 43 x -= lowbit(x); 44 } 45 return sum; 46 } 47 48 int main() 49 { 50 while (scanf("%d", &n) != EOF && n) 51 { 52 for (int i = 1; i <= n; ++i) 53 { 54 scanf("%d", &node[i].val); 55 node[i].pos = i; 56 } 57 sort(node + 1, node + n + 1, cmp); //排序 58 for (int i = 1; i <= n; ++i) reflect[node[i].pos] = i; //离散化 59 for (int i = 1; i <= n; ++i) c[i] = 0; //初始化树状数组 60 long long ans = 0; 61 for (int i = 1; i <= n; ++i) 62 { 63 update(reflect[i]); 64 ans += i - getsum(reflect[i]); 65 } 66 printf("%lld\n", ans); 67 } 68 return 0; 69 }

转载自

给定n个数,要求这些数构成的逆序对的个数。除了用归并排序来求逆序对个数,还可以使用树状数组来求解。

树状数组求解的思路:开一个能大小为这些数的最大值的树状数组,并全部置0。从头到尾读入这些数,每读入一个数就更新树状数组,查看它前面比它小的已出现过的有多少个数sum,然后用当前位置减去该sum,就可以得到当前数导致的逆序对数了。把所有的加起来就是总的逆序对数。

题目中的数都是独一无二的,这些数最大值不超过999999999,但n最大只是500000。如果采用上面的思想,必然会导致空间的巨大浪费,而且由于内存的限制,我们也不可能开辟这么大的数组。因此可以采用一种称为“离散化”的方式,把原始的数映射为1-n一共n个数,这样就只需要500000个int类型的空间。

离散化的方式:

struct Node

{

int val;

int pos;

};

Node node[500005];

int reflect[500005];

val存放原数组的元素,pos存放原始位置,即node[i].pos = i。

把这些结构体按照val的大小排序。

reflect数组存放离散化后的值,即reflect[node[i].pos] = i。

这样从头到尾读入reflect数组中的元素,即可以保持原来的大小关系,又可以节省大部分空间。

 

这个可以辅助理解

树状数组实际上还是一个数组,只不过它的每个元素保存了跟原来数组的一些元素相关的结合值。

若A为原数组,定义数组C为树状数组。C数组中元素C[ i ]表示A[ i –lowbit( i ) + 1]至A[ i ]的结合值。

lowbit(i)是i的二进制中最后一个不为零的位数的2次方,可以这样计算

lowbit(i)=x&(-x)

lowbit(i)=x&(x^(x-1))

转载于:https://www.cnblogs.com/radiumlrb/p/5811757.html

你可能感兴趣的文章
AU3脚本自动安装QQ6.7
查看>>
Win8Metro(C#)数字图像处理--2.3图像反色
查看>>
#define的用法
查看>>
即时通讯-没有那么可怕
查看>>
ES6: 字符串的扩展
查看>>
统计系统剩余的内存、数据类型转换计算(计算mac地址)、数据类型转换(列表与字典相互转换)...
查看>>
DS5100,DS5300磁盘配置注意事项
查看>>
gzip的工作原理及在Nginx和Apache服务中的应用
查看>>
六步实现Rest风格的API
查看>>
Centos7手动安装OpenStack Mitaka版本--KeyStone安装
查看>>
order by不走索引的思考
查看>>
PHP常用函数
查看>>
摄像头变成文字扫描器
查看>>
**PHP分步表单提交思路(分页表单提交)
查看>>
鱼鹰软件签约活动管理专家蓝色方略
查看>>
Scott Schema创建脚本
查看>>
软件开发所用bug管理系统之mantis之windows版
查看>>
RHEL/CentOS6.6SSHD服务安装、配置、使用
查看>>
报错nginx failed error: during websocket handshake
查看>>
爬姓名大全网站的姓名
查看>>