首页 > 其他分享 >关于归并排序求逆序对

关于归并排序求逆序对

时间:2023-10-09 21:14:27浏览次数:57  
标签:归并 int ll mid msort 序列 排序 逆序

之前写了一篇 blog 讲如何用归并排序求逆序对以及解决相关问题。最近才发现自己根本没搞懂,而且写的不好。遂重写。


前言:什么是逆序对?

对于数列的第 i 个和第 j 个元素,若满足 i < j 且 a[i] > a[j],则其为一个逆序对。


归并排序的过程:将序列分为两部分,先递归将两侧序列排序,后将两个有序序列合并。

合并两个序列时,由于已经是有序序列,所以用双指针,一个从 l 指向 mid,一个从 mid + 1 指向 r。两个指针所指的,不断将更小的那个放到来转运的新序列 b 中。若相等,则优先将前一序列中的元素先放入(保证稳定性)。把剩下的塞进去,最后把新序列里的东西倒腾回去。

这是一个归并排序的码:

void msort(int l, int r)
{
    if(l >= r) return;
    int mid = (l + r) >> 1;
    msort(l, mid);
    msort(mid + 1, r);//递归排序
    int k = 0;
    int i = l, j = mid + 1;
    while(i <= mid && j <= r)
    {
        if(a[i] <= a[j]) b[++ k] = a[i ++];//稳定性
        else b[++ k] = a[j ++];
    }
    while(i <= mid) b[++ k] = a[i ++];//把剩余的部分加进去
    while(j <= r) b[++ k] = a[j ++];
    for(int i = l; i <= r; i ++ ) a[i] = b[i - l + 1];//最后转移到原数组,完成合并
}

逆序对到底怎么求?其实看看这个合并的过程,不难发现过程中就体现出的逆序对。

合并的两段有序序列,a[i] 表示前段,a[j] 表示后段。

合并过程中若 a[j] < a[i] ,则说明构成逆序对。由于两段有序,所以从 a[i] ~ a[mid] 这一段的所有元素都与 a[j] 构成逆序对。

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N = 1e5 + 10;
int n;
ll ans;
int a[N], b[N];
void msort(int l, int r)
{
    if(l >= r) return;
    int mid = (l + r) >> 1;
    msort(l, mid);
    msort(mid + 1, r);
    int k = 0, i = l, j = mid + 1;
    while(i <= mid && j <= r)
    {
        if(a[i] <= a[j]) b[++ k] = a[i ++];
        else
        {
            b[++ k] = a[j ++];
            ans += mid - i + 1;
        }
    }
    while(i <= mid) b[++ k] = a[i ++];
    while(j <= r) b[++ k] = a[j ++];
    for(int i = l; i <= r; i ++ ) a[i] = b[i - l + 1];
}
int main()
{
    scanf("%d", &n);
    for(int i = 1; i <= n; i ++ ) scanf("%d", &a[i]);
    msort(1, n);
    printf("%lld", ans);
}

1.板子题:洛谷 P5149 会议座位

好久之前学字典树时就加到题单里了,但由于忘了逆序对怎么求了(?)所以一直没写。今天一看发现是板子。

读完题你就发现这不就是求逆序对吗。

其实用字典树麻烦了。你只需要将每个字符串 map 转换成数字然后套板子。

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N = 1e5 + 10;
ll ans;
int n;
int a2[N], b[N];
string s;
map<string, int> a1;
void msort(int l, int r)
{
    if(l >= r) return;
    int mid = (l + r) >> 1;
    msort(l, mid);
    msort(mid + 1, r);
    int k = 0, i = l, j = mid + 1;
    while(i <= mid && j <= r)
    {
        if(a2[i] <= a2[j]) b[++ k] = a2[i ++];
        else
        {
            b[++ k] = a2[j ++];
            ans += mid - i + 1;
        }
    }
    while(i <= mid) b[++ k] = a2[i ++];
    while(j <= r) b[++ k] = a2[j ++];
    for(int i = l; i <= r; i ++ ) a2[i] = b[i - l + 1];
}
int main()
{
    scanf("%d", &n);
    for(int i = 1; i <= n; i ++ )
    {
        cin >> s;
        a1[s] = i;
    }
    for(int i = 1; i <= n; i ++ )
    {
        cin >> s;
        a2[i] = a1[s];
    }
    msort(1, n);
    printf("%lld", ans);
}

2.P2717 寒假作业

题意:长度为 n 正整数序列,求有多少连续子序列的平均值不小于 k。

暴力的想的话即用前缀和统计,暴力枚举 l 和 r。每一个 \(s[j]-s[i-1]>=k\) 都对答案做出贡献。

然后看这个式子。我们改变一下:\(s[j]-k-(s[i-1]-k)>=0\)。

然后发现如果把前缀和统计在最初的时候就减去 k 的话统计的就是 \(s[j]-s[i-1]>=0\),移项得到 \(s[j]>=s[i-1]\)。

发现很眼熟,就能发现这就是求顺序对。(总方案数减去逆序对个数)。

一种比较牛逼的策略。

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=1e5+10;
int n,k;
int a[N];
ll b[N],s[N],ans;
void msort(int l,int r){
	if(l>=r) return;
	int mid=(l+r)>>1;
	msort(l,mid),msort(mid+1,r);
	int k=0,i=l,j=mid+1;
	while(i<=mid&&j<=r){
		if(s[i]<=s[j]) b[++k]=s[i++];
		else{
			b[++k]=s[j++];
			ans+=mid-i+1;
		}
	}
	while(i<=mid) b[++k]=s[i++];
	while(j<=r) b[++k]=s[j++];
	for(int i=l;i<=r;i++) s[i]=b[i-l+1];
}
int main(){
	scanf("%d%d",&n,&k);
	for(int i=1;i<=n;i++) scanf("%d",&a[i]);
	for(int i=1;i<=n;i++) s[i]=s[i-1]+a[i]-k;
	msort(0,n);
	printf("%lld",(ll)((ll)n*(n+1)/2)-ans); 
}

四倍经验:P7868 [COCI2015-2016#2] VUDU[ARC075E] Meaningful MeanP2804 神秘数字

以后有好题的话会及时补充。

标签:归并,int,ll,mid,msort,序列,排序,逆序
From: https://www.cnblogs.com/Moyyer-suiy/p/17753140.html

相关文章

  • 排序数组
       排序数组 数组C++JavaPython前言本题你可以选择直接调用库函数来对序列进行排序,但意义不大。由于排序算法有很多,本文只介绍三种常见的基于比较的复杂度较低的排序。方法一:快速排序思路和算法快速排序的主要思想是通过划分将待排序的序列分成前后两......
  • 数据重整:用Java实现精准Excel数据排序的实用策略
    摘要:本文由葡萄城技术团队原创并首发。转载请注明出处:葡萄城官网,葡萄城为开发者提供专业的开发工具、解决方案和服务,赋能开发者。前言在数据处理或者数据分析的场景中,需要对已有的数据进行排序,在Excel中可以通过排序功能进行整理数据。而在Java中,则可以借助Excel表格插件对数......
  • Map根据value排序取topN
    publicstaticvoidmain(String[]args){Map<String,Integer>map=newHashMap<>();/*for(inti=0;i<1000000;i++){intnextInt=newRandom().nextInt();map.put("A"+i,i*nextInt......
  • C#归并排序算法
    前言归并排序是一种常见的排序算法,它采用分治法的思想,在排序过程中不断将待排序序列分割成更小的子序列,直到每个子序列中只剩下一个元素,然后将这些子序列两两合并并排序,最终得到一个有序的序列。归并排序实现原理将待排序序列分割成两个子序列,直到每个子序列中只有一个元素。......
  • 08_三个数字排序
    三个数字排序!/bin/bashread-p"请输入一个整数:"num1read-p"请输入一个整数:"num2read-p"请输入一个整数:"num3#不管谁大谁小,最后都打印echo"$num1,$num2,$num3"#num1中永远存最小的值,num2中永远存中间值,num3永远存最大值tmp=0if[$num1-gt$num2];t......
  • 递归分治法在快速排序中的应用 java以界面的方式实现
    递归分治法在快速排序中的应用 分治法的基本思想§分治法的基本思想是将一个规模为n的问题分解为k个规模较小的子问题,这些子问题互相独立且与原问题相同。 k个子问题分别求解。如果子问题的规模仍然不够小,则再划分为k个子问题,如此递归的进行下去,直到问题规模足够小,很容易求......
  • 输入若干个数值存入数组中,采用冒泡算法进行升序或降序排序
    [12:38:09root@centos8~]#bashsort.shbeforesort:1475626459133973060324422175901602255661082520888121022092421146668557255975852542867817400aftersort:3060328678264592442220888175901740016022147561339711466108259758924272......
  • 前端歌谣的刷题之路-第三十七题-从大到小排序
     目录前言题目编辑 核心代码总结前言我是歌谣我有个兄弟巅峰的时候排名c站总榜19叫前端小歌谣曾经我花了三年的时间创作了他现在我要用五年的时间超越他今天又是接近兄弟的一天人生难免坎坷大不了从头再来歌谣的意志是永恒的放弃很容易但是坚持一定很酷本题目源自于牛......
  • 05_用一个栈实现另一个栈的排序
    用一个栈实现另一个栈的排序【题目】一个栈中的元素的类型为整型,现在想将该栈从顶到底按从大到小的顺序排序,只许申请一个栈。除此之外,可以申请新的变量,但不能申请额外的数据结构。如何完成排序?【解答】将要排序的栈记为stack,申请的辅助栈记为help。在stack上执行pop操作,弹出的......
  • sqlserver递归排序
    主要介绍了sqlserver递归排序相关的知识,希望对你有一定的参考价值。此算法不支持无限递归,只支持指定最大层级,实际应用中,一般不会超过5级,sqlserver最大只支持100级。递归层级LevelOrder序号,每层级最大序号sequences,子级序号=父级序号+父级序号/最大序号即LevelOrder=p.LevelO......