归并排序,树状数组 两种方法求逆序对
2015-08-24 21:42
429 查看
我们知道,求逆序对最典型的方法就是树状数组,但是还有一种方法就是Merge_sort(),即归并排序。
实际上归并排序的交换次数就是这个数组的逆序对个数,为什么呢?
我们可以这样考虑:
归并排序是将数列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。因此,可以在归并
排序中的合并过程中计算逆序数.
题目:http://poj.org/problem?id=1804
题意:给定一个序列a[],每次只允许交换相邻两个数,最少要交换多少次才能把它变成非递降序列.
树状数组求逆序:
http://poj.org/problem?id=2299
思路: 离散化+树状数组
分析:
1 题目的意思就是要求逆序数对
2 题目的输入个数有500000的规模但是每个数的最大值为999999999,因此我们需要离散化这些数据
3 对于数据9 1 0 5 4我们离散化成5 2 1 4 3
那么对于输入一个树a[i]我们去求一下它的离散化后的id,然后去求前面比这个id大的个数
4 由于getSum(x)函数的求和是求[1,x]而不是[x,MAXN),所以我们可以换成求小于等于id的个数即getSum(id),然后i-1-getSum(id)就是比id大的个数,最后在更新一下treeNum[id]
代码:
实际上归并排序的交换次数就是这个数组的逆序对个数,为什么呢?
我们可以这样考虑:
归并排序是将数列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。因此,可以在归并
排序中的合并过程中计算逆序数.
题目:http://poj.org/problem?id=1804
题意:给定一个序列a[],每次只允许交换相邻两个数,最少要交换多少次才能把它变成非递降序列.
#include <iostream> #include <string.h> #include <stdio.h> using namespace std; const int N = 1005; int a ,tmp ; int ans; void Merge(int l,int m,int r) { int i = l; int j = m + 1; int k = l; while(i <= m && j <= r) { if(a[i] > a[j]) { tmp[k++] = a[j++]; ans += m - i + 1; } else { tmp[k++] = a[i++]; } } while(i <= m) tmp[k++] = a[i++]; while(j <= r) tmp[k++] = a[j++]; for(int i=l;i<=r;i++) a[i] = tmp[i]; } void Merge_sort(int l,int r) { if(l < r) { int m = (l + r) >> 1; Merge_sort(l,m); Merge_sort(m+1,r); Merge(l,m,r); } } int main() { int n,T,tt=1; scanf("%d",&T); while(T--) { scanf("%d",&n); for(int i=0;i<n;i++) scanf("%d",&a[i]); ans = 0; Merge_sort(0,n-1); printf("Scenario #%d:\n%d\n\n",tt++,ans); } return 0; }
树状数组求逆序:
http://poj.org/problem?id=2299
思路: 离散化+树状数组
分析:
1 题目的意思就是要求逆序数对
2 题目的输入个数有500000的规模但是每个数的最大值为999999999,因此我们需要离散化这些数据
3 对于数据9 1 0 5 4我们离散化成5 2 1 4 3
那么对于输入一个树a[i]我们去求一下它的离散化后的id,然后去求前面比这个id大的个数
4 由于getSum(x)函数的求和是求[1,x]而不是[x,MAXN),所以我们可以换成求小于等于id的个数即getSum(id),然后i-1-getSum(id)就是比id大的个数,最后在更新一下treeNum[id]
代码:
#include<cstdio> #include<cstring> #include<iostream> #include<algorithm> #include<vector> using namespace std; const int MAXN = 500010; int n; int tmpNum[MAXN] , num[MAXN]; int treeNum[MAXN]; int lowbit(int x){ return x&(-x); } int getSum(int x){ int sum = 0; while(x){ sum += treeNum[x]; x -= lowbit(x); } return sum; } void add(int x , int val){ while(x < MAXN){ treeNum[x] += val; x += lowbit(x); } } long long solve(){ long long ans = 0; memcpy(tmpNum , num , sizeof(num)); memset(treeNum , 0 , sizeof(treeNum)); sort(num+1 , num+1+n); int len = unique(num+1 , num+1+n)-(num+1); for(int i = 1 ; i <= n ; i++){ int id = lower_bound(num+1 , num+1+n,tmpNum[i])-num; ans += i-getSum(id)-1; add(id , 1); } return ans; } int main(){ while(scanf("%d" , &n) && n){ for(int i = 1 ; i <= n ; i++) scanf("%d" , &num[i]); printf("%lld\n" , solve()); } return 0; }
相关文章推荐
- 将Python脚本打包成可执行文件
- 毕业日记——科大两年
- 跳台阶
- 欢迎使用CSDN-markdown编辑器
- 项目A 第一天 抓接口 测试数据
- java.exe和javaw.exe的区别
- C语言基础--进制
- java新手笔记29 读取文件
- ruby 资料整理
- android反编译
- 如果荣耀7和荣耀7i整合在一起?
- 将Python脚本打包成可执行文件
- Cannot set TextView width using setWidth or setLayoutParams
- UVA 10391 Compound Words
- 欧拉函数
- Apache 配置 Basic 认证
- Linux命令之文件管理命令cp、mv、rm
- 2.7-Apache通过rewrite限制某个目录
- vs2013 中HTML页 无法在设计窗口中查看的解决
- button变成href (即按钮超链效果)