归并排序+归并排序求逆序对(例题P1908)
归并排序(merge sort)
顾名思义,这是一种排序算法,时间复杂度为O(nlogn),时间复杂度上和快排一样
归并排序是分治思想的应用,我们先将n个数不断地二分,最后得到n个长度为1的区间,显然,这n个小区间都是单调的,随后合并相邻的两个区间,得到n/2个单增(减)的区间,随后我们继续合并相邻的两个区间,得到n/4个单增(减)的区间....
每次合并操作的总时间复杂度为O(n),logn次合并用时O(logn),故总时间复杂度为O(nlogn)
合并操作比较好理解,就像下图这样二分区间即可(红线代表分割线):
然后,我们要如何实现O(n)的复杂度实现区间合并呢?
我们另开一个大小和原数组a大小一样的数组alt,存储需要合并的两个区间的数,方便起见,我们用pos代表alt数组的当前指向的位置,用i表示左区间当前所指的位置,用j表示右区间当前所指的位置,如下图所示:
记此时我们合并形成的区间为[l,r],按升序排序,那么我们枚举这一区间中的pos,每次比较alt[i]和alt[j],如果alt[i] < alt[j] 那么令a[pos] = alt[i],同时pos++,i++ ,否则令a[pos] = alt[j] ,同时pos++,j++,如果左区间的数已经全部遍历,那么将右区间剩下的数依次加入pos位置,反之同理,操作过程如下图所示:
至此,区间[l,r]这一段区间已经完成排序,这就是归并排序的合并过程
归并排序代码
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<queue>
#include<string>
#include<fstream>
#include<vector>
#include<stack>
#include <map>
#include <iomanip> #define bug cout << "**********" << endl
#define show(x, y) cout<<"["<<x<<","<<y<<"] "
#define LOCAL = 1;
using namespace std;
typedef long long ll;
const int inf = 1e9 + ;
const ll mod = 1e9 + ;
const int Max = 5e5 + ; int n;
ll sum;
int alt[Max]; void merge(int a[], int l, int r)
{
for(int i= l; i <= r; i ++)
{
alt[i] = a[i];
}
int mid = (l + r) >> ;
int i = l, j = mid + ;
for (int pos = l; pos <= r; pos++)
{
if (i == mid + )
{
a[pos] = alt[j];
j++;
}
else if (j == r + )
{
a[pos] = alt[i];
i++;
}
else if (alt[i] > alt[j])
{
a[pos] = alt[j];
j++;
}
else
{
a[pos] = alt[i];
i++;
}
}
} void merge_sort(int a[], int l, int r)
{
if (l == r)
return;
int mid = (l + r) >> ;
merge_sort(a, l, mid);
merge_sort(a, mid + , r);
merge(a, l, r);
} int a[Max]; int main()
{
#ifdef LOCAL
// freopen("input.txt", "r", stdin);
// freopen("output.txt", "w", stdout);
#endif
sum = ;
scanf("%d", &n);
for (int i = ; i < n; i++)
scanf("%d", a + i);
merge_sort(a, , n - );
for(int i = ;i < n ;i ++)
printf("%d%c",a[i],i == n-?'\n':' ');
return ;
}
利用归并排序求逆序对
我们注意到在归并排序过程中,我们有一步判断:if(alt[i] > alt[j]) ,如果判断为真,那么显然,j 和 区间[i,mid]每一个点都形成逆序对,一共mid-i+1个,而且只在这个地方会出现形成逆序对的情况,那么情况就很简单了,我们将原数组进行归并排序,并在if(alt[i] > alt[j] ) 为真的时候,统计一下逆序对的个数即可。
代码区
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<queue>
#include<string>
#include<fstream>
#include<vector>
#include<stack>
#include <map>
#include <iomanip> #define bug cout << "**********" << endl
#define show(x, y) cout<<"["<<x<<","<<y<<"] "
#define LOCAL = 1;
using namespace std;
typedef long long ll;
const int inf = 1e9 + ;
const ll mod = 1e9 + ;
const int Max = 5e5 + ; int n;
ll sum;
int alt[Max]; void merge(int a[], int l, int r)
{
for(int i= l; i <= r; i ++)
{
alt[i] = a[i];
}
int mid = (l + r) >> ;
int i = l, j = mid + ;
for (int pos = l; pos <= r; pos++)
{
if (i == mid + )
{
a[pos] = alt[j];
j++;
}
else if (j == r + )
{
a[pos] = alt[i];
i++;
}
else if (alt[i] > alt[j])
{
a[pos] = alt[j];
j++;
sum += mid - i + ; //i及其此后的都可以和a[j]形成逆序对
}
else
{
a[pos] = alt[i];
i++;
}
}
} void merge_sort(int a[], int l, int r)
{
if (l == r)
return;
int mid = (l + r) >> ;
merge_sort(a, l, mid);
merge_sort(a, mid + , r);
merge(a, l, r);
} int a[Max]; int main()
{
#ifdef LOCAL
// freopen("input.txt", "r", stdin);
// freopen("output.txt", "w", stdout);
#endif
sum = ;
scanf("%d", &n);
for (int i = ; i < n; i++)
scanf("%d", a + i);
merge_sort(a, , n - );
printf("%lld\n", sum);
return ;
}
归并排序+归并排序求逆序对(例题P1908)的更多相关文章
- AC日记——codevs 1688 求逆序对
1688 求逆序对 时间限制: 1 s 空间限制: 128000 KB 题目等级 : 黄金 Gold 题解 查看运行结果 题目描述 Description 给定一个序列a1,a2,…, ...
- HDU 3743 Frosh Week(归并排序求逆序对)
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3743 题目意思就是给你一个长为n的序列,让你求逆序对.我用的是归并排序来求的.归并排序有一个合并的过程 ...
- 浙江工商大学15年校赛I题 Inversion 【归并排序求逆序对】
Inversion Time Limit 1s Memory Limit 131072KB Judge Program Standard Ratio(Solve/Submit) 15.00%(3/20 ...
- 2014 HDU多校弟五场A题 【归并排序求逆序对】
这题是2Y,第一次WA贡献给了没有long long 的答案QAQ 题意不难理解,解题方法不难. 先用归并排序求出原串中逆序对的个数然后拿来减去k即可,如果答案小于0,则取0 学习了归并排序求逆序对的 ...
- Day2:T4求逆序对(树状数组+归并排序)
T4: 求逆序对 A[I]为前缀和 推导 (A[J]-A[I])/(J-I)>=M A[j]-A[I]>=M(J-I) A[J]-M*J>=A[I]-M*I 设B[]=A[]-M*( ...
- 归并排序&&归并排序求逆序对
归并排序 归并排序(MERGE-SORT)是建立在归并操作上的一种有效的排序算法,该算法是采用分治法(Divide and Conquer)的一个非常典型的应用.将已有序的子序列合并,得到完全有序的序 ...
- 用归并排序或树状数组求逆序对数量 poj2299
题目链接:https://vjudge.net/problem/POJ-2299 推荐讲解树状数组的博客:https://blog.csdn.net/int64ago/article/details/ ...
- POJ 3067 - Japan - [归并排序/树状数组(BIT)求逆序对]
Time Limit: 1000MS Memory Limit: 65536K Description Japan plans to welcome the ACM ICPC World Finals ...
- 【BZOJ4769】超级贞鱼 归并排序求逆序对
[BZOJ4769]超级贞鱼 Description 马达加斯加贞鱼是一种神奇的双脚贞鱼,它们把自己的智慧写在脚上——每只贞鱼的左脚和右脚上各有一个数.有一天,K只贞鱼兴致来潮,排成一列,从左到右第i ...
随机推荐
- luogu2634
P2634 [国家集训队]聪聪可可 题目描述 聪聪和可可是兄弟俩,他们俩经常为了一些琐事打起来,例如家中只剩下最后一根冰棍而两人都想吃.两个人都想玩儿电脑(可是他们家只有一台电脑)……遇到这种问题,一 ...
- 【java设计模式】-07适配器模式
适配器模式 定义: 将一个类的接口转换成客户希望的另外一个接口.适配器模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作. 类型: 结构型模式 应用实例: 1.JAVA JDK 1.1 提供 ...
- SQL Labs刷题补坑记录(less54-less65)
LESS54: 只有10次尝试,dump处secret key 直接union 查就可以,括号为单引号闭合 LESS55: 尝试出来闭合的方式为)括号,后面操作与54相同 LESS56: 尝试出来括号 ...
- css实现元素在div底部显示
#CSS .1 {position:relative;} .2 {;} #HTML <div class="1"> <div class="2" ...
- Netfilter 之 iptable_mangle
初始化 iptable_mangle_table_init函数通过调用ipt_register_table完成mangle表注册和钩子函数注册的功能:该流程与iptable_filter的函数调用的函 ...
- DS博客作业07——查找
1.本周学习总结 1.1思维导图 1.2.谈谈你对查找运算的认识及学习体会. 查找的内容和前两章树和图相比,要简单许多,在二次搜索树虽然也要用到树,但是也是很简单的树的操作,再加上查找的代码可以使用m ...
- JS 省市联动 ajax
省市联动 //省市联动 $('.locationCode').change(function () { var val = $(this).val(); if ...
- Linux | linux的那些常见目录
1. bin目录 binary(二进制的):许多"指令"对应的可"执行程序文件"目录 2. sbin目录 说明:super binary 超级的 二进制 许多& ...
- qt+opencv编译环境的配置
第一步:安装与配置Qt 从https://account.qt.io/downloads下载Qt,并注册账户.开始安装.首先下载qt-opensource-windows-x86-mingw482_o ...
- SpringCloud(二)之Ribbon的实现负载均衡的基本应用
一 Ribbon简介 Ribbon是Netflix发布的负载均衡器,它是一个基于HTTP和TCP的客户端负载均衡工具,它基于Netflix Ribbon实现.通过Spring Cloud的封装 ...