首页 > 编程语言 >《算法竞赛进阶指南》0x05排序

《算法竞赛进阶指南》0x05排序

时间:2024-07-24 22:28:42浏览次数:14  
标签:cnt 进阶 int 0x05 long ++ MAX 排序


在程序设计中通常会用到以下排序:
1.选择排序、插入排序、冒泡排序
2.堆排序、归并排序、快速排序
3.计数排序、基数排序、桶排序

前两类排序时基于比较的排序,第一类排序的时间复杂度为 O ( n 2 ) O(n^2) O(n2),第二类排序的时间复杂度为 O ( n l o n g n ) 。 O(nlongn)。 O(nlongn)。第三类排序不直接比较大小,而是对被排序的数值按照位划分,分类映射等处理方式,其时间复杂度不仅与n有关,还与数值的大小范围m有关。

离散化

假设问题涉及int范围内的n个整数a[1]-a[n],这n个整数可能重复,去重后共有m个。我们要把每个整数用1-m中的整数代替,并且保证大小顺序不变。

void discrete()
{
	sort(a+1,a+n+1);
	for(int i=1;i<=n;i++)
	{
		if(i==1||a[i]!=a[i-1])
		b[++m]=a[i];
	}
}
int query(int x)
{
	return lower_bound(b+1.b+m+1,x)-b;
}

例题
acwing103.电影

将2*m+n个可能涉及到的语言存到数组中,并进行离散化处理。对于每一种语言对应观众的数量进行计数。挨个看每一个电影对应的高兴和比较高兴的观众的数量,选择出最优的。

#include<iostream>
#include<algorithm>
using namespace std;
#define MAX_N 200000
int all[MAX_N*3+5];
int a[MAX_N+5];
int b1[MAX_N+5],b2[MAX_N+5];
int l[MAX_N*3+5];
int cnt[MAX_N+5];
int score1[MAX_N+5],score2[MAX_N+5];
int r=0,q=0;
int n,m;
int query(int x)
{
    return lower_bound(l+1,l+q+1,x)-l;
}
int main()
{
    cin>>n;
    for(int i=1;i<=n;i++)
    {
        scanf("%d",a+i);
        all[++r]=a[i];
    }
    cin>>m;
    for(int i=1;i<=m;i++)
    {
        scanf("%d",b1+i);
        all[++r]=b1[i];
    }
    for(int i=1;i<=m;i++)
    {
        scanf("%d",b2+i);
        all[++r]=b2[i];
    }
    sort(all+1,all+r+1);
    for(int i=1;i<=n+2*m;i++)
    {
        if(i==1||all[i]!=all[i-1])
        l[++q]=all[i];
    }
    for(int i=1;i<=n;i++)
    {
    	int x=query(a[i]);
    	cnt[x]+=1;
	}

	int t1=-1,t2=-1,t3;
	for(int i=1;i<=m;i++)
	{
		int s1=cnt[query(b1[i])];
		int s2=cnt[query(b2[i])];
		if(s1>t1||(s1==t1&&s2>t2))
		{
			t3=i;
			t1=s1;
			t2=s2;
		}
	}
	cout<<t3;
    return 0;
}

中位数

在有序序列中,中位数有一些优美的性质,可以引出一系列与他相关的问题。动态维护中位数也非常值得探讨。

例题
acwing104.货舱选址

在N为奇数时,货仓建在A[(N+1)/2];
在N为偶数时,货仓建在A[N/2]~A[N/2+1]。
因此不论哪种情况建在A[(N+1)/2]为最优。

#include<iostream>
#include<algorithm>
using namespace std;
#define MAX_N 100000
int n;
int a[MAX_N+5];
int main()
{
    cin>>n;
    for(int i=1;i<=n;i++)
    scanf("%d",a+i);
    sort(a+1,a+n+1);
    int t=a[(n+1)/2];
    int ans=0;
    for(int i=1;i<=n;i++)
    ans+=abs(a[i]-t);
    cout<<ans;
    return 0;
}

acwing105.七夕祭

交换左右两个相邻摊点只会改变某两列cl感兴趣的摊点数,交换上下只会改变某两行感兴趣的摊点数,所以可以把本题分成两个部分。
1.通过最少次数的左右交换使每列中cl感兴趣的摊点数相同。
2.通过最少次数的上下交换使每行中cl感兴趣的摊点数相同。
经计算 ∣ x 1 ∣ + ∣ x 2 ∣ + . . . + ∣ x n ∣ = ∣ x 1 − c 1 ∣ + ∣ x 2 − c 2 ∣ + ∣ x n − c n ∣ 。其中 c k = ( k − 1 ) ∗ ∑ i = 1 k − 1 a i + ( k − 1 ) ∗ a ( a 为平均数 ) |x1|+|x2|+...+|xn|=|x1-c1|+|x2-c2|+|xn-cn|。其中ck=(k-1)*\sum\limits_{i=1}^{k-1}ai+(k-1)*a(a为平均数) ∣x1∣+∣x2∣+...+∣xn∣=∣x1−c1∣+∣x2−c2∣+∣xn−cn∣。其中ck=(k−1)∗i=1∑k−1​ai+(k−1)∗a(a为平均数)因此可以把该问题转化为“货仓选址”。

#include<iostream>
#include<algorithm>
using namespace std;
#define MAX_N 100000
int n,m,t;
int row[MAX_N+5],col[MAX_N+5];
int sum[MAX_N+5];
int c[MAX_N+5];
long long solve(int l,int*a)
{
    if(t%l)return -1;
    int avg=t/l;
    for(int i=1;i<=l;i++)
    {
        sum[i]=sum[i-1]+a[i];
        c[i]=(i-1)*avg-sum[i-1];
    }
    for(int i=1;i<=l;i++)
    {
        c[i]=(i-1)*avg-sum[i-1];
    }
    sort(c+1,c+l+1);
    int p=c[(l+1)/2];
    long long ans=0;
    for(int i=1;i<=l;i++)
    ans+=1ll*abs(c[i]-p);
    return ans;
}
int main()
{
    cin>>n>>m>>t;
    for(int i=1,x,y;i<=t;i++)
    {
        scanf("%d%d",&x,&y);
        row[x]++;
        col[y]++;
    }
    long long q1=solve(n,row);
    long long q2=solve(m,col);
    if(q1==-1&&q2==-1)
    cout<<"impossible";
    else if(q1==-1)
    cout<<"column"<<" "<<q2;
    else if(q2==-1)
    cout<<"row"<<" "<<q1;
    else cout<<"both"<<" "<<q1+q2;
    return 0;
}

acwing106.动态中位数

使用一个大顶堆和一个小顶堆。序列从小到大排名1~M/2存到大顶堆,M/2+1~M的存在小顶堆。

#include<iostream>
#include<queue>
using namespace std;
#define MAX_N 100000
int t;
int arr[MAX_N+5];
int main()
{
    cin>>t;
    while(t--)
    {
        int id,m,cnt=0;
        scanf("%d %d",&id,&m);
        printf("%d %d\n",id,(m+1)/2);
        priority_queue<int>down;
        priority_queue<int,vector<int>,greater<int>>up;
        for(int i=1,x;i<=m;i++)
        {
            scanf("%d",&x);
            if(down.size()==0||x<down.top())down.push(x);
            else up.push(x);
            if(down.size()>up.size()+1)up.push(down.top()),down.pop();
            if(up.size()>down.size())down.push(up.top()),up.pop();
            if(i%2)
            {
                printf("%d ",down.top());
                if(++cnt%10==0)printf("\n");
            }
        }
        if(cnt%10)printf("\n");
    }
    return 0;
}

第K大数

给定n个数,如何求出第k大数?
从大到小进行快速排序的思想是,在每一层递归中,随机选取一个数为基准,把比它大的交换到左半段,其余的和基准值自身一起作为右半段,然后继续递归对两边分别排序,平均情况下快速排序的复杂度为 O ( n l o g n ) O(nlogn) O(nlogn)。
实际上在选出来基准值之后,我们可以统计处大于基准值的数的数量cnt,如 k ≤ c n t k\leq cnt k≤cnt,我们就在左半边寻找第k大数;如果k>cnt,我们就在右半边寻找第k-cnt大数。在平均情况下,时间复杂度是 n + n / 2 + n / 4 + . . . + 1 = O ( n ) n+n/2+n/4+...+1=O(n) n+n/2+n/4+...+1=O(n)。

逆序对

使用归并排序可以在 O ( l o g n ) O(logn) O(logn)时间内求出来一个长度为n的序列中逆序对的个数。归并排序每次把序列二分,递归对左右两个半边排序,然后合并两个序列。

例题
acwing107.超快速排序

#include<iostream>
using namespace std;
#define MAX_N 500000
long long a[MAX_N+5],t[MAX_N+5];
long long merge_sort(int l,int r)
{
    if(l==r)return 0;
    int mid=l+r>>1;
    long long ret=merge_sort(l,mid)+merge_sort(mid+1,r);
    int i=l,j=mid+1,k=0;
    while(i<=mid&&j<=r)
    {
        if(a[i]<=a[j])t[++k]=a[i++];
        else if(a[i]>a[j])
        {
            ret+=(mid-i+1);
            t[++k]=a[j++];
        }
    }
    while(i<=mid)t[++k]=a[i++];
    while(j<=r)t[++k]=a[j++];
    for(int i=l,j=1;i<=r;i++,j++)a[i]=t[j];
    return ret;
}
int main()
{
    int n;
    while(scanf("%d",&n),n)
    {
        for(int i=1;i<=n;i++)
        scanf("%lld",a+i);
        printf("%lld\n",merge_sort(1,n));
    }
    return 0;
}

acwing108.奇数码游戏

对于n为奇数的情况,两个局面可相互到达等价于把各个方格的数依次写成一行,逆序对个数奇偶性相同。
对于n为偶数地情况,两个局面可相互到达等价于把各个方格的数依次写成一行,逆序对个数奇之差和两个局面下空格所在的行数之差奇偶性相同。
在n*m网格上,也服从上面两个结论之一(根据列数奇偶性分类讨论)。

#include<iostream>
using namespace std;
#define MAX_N 500
int arr[MAX_N*MAX_N+5];
int t[MAX_N*MAX_N+5];
long long merge(int l,int r)
{
    if(l==r)return  0;
    int mid=(l+r)>>1;
    int i=l,j=mid+1,k=0;
    long long res=merge(l,mid)+merge(mid+1,r);
    while(i<=mid&&j<=r)
    {
        if(arr[i]<=arr[j])t[++k]=arr[i++];
        else{
            res+=(mid-i+1);
            t[++k]=arr[j++];
        }
    }
    while(i<=mid)t[++k]=arr[i++];
    while(j<=r)t[++k]=arr[j++];
    for(int i=l,j=1;i<=r;i++,j++)
    arr[i]=t[j];
    return res;
}
int main()
{
    int n,a;
    while(scanf("%d",&n)!=EOF)
    {
        int cnt=0;
        if(n==1)
        {
            scanf("%d%d",&a,&a);
            printf("TAK\n");
            continue;
        }
        for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
        {
            scanf("%d",&a);
            if(a==0)continue;
            arr[++cnt]=a;
        }
        long long q1=merge(1,cnt);
        cnt=0;
        for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
        {
            scanf("%d",&a);
            if(a==0)continue;
            arr[++cnt]=a;
        }
        long long q2=merge(1,cnt);
        if((q1&1)==(q2&1))printf("TAK\n");
        else printf("NIE\n");
    }
    return 0;
}

标签:cnt,进阶,int,0x05,long,++,MAX,排序
From: https://blog.csdn.net/m0_70897036/article/details/140465463

相关文章

  • 七大基于比较的排序算法
    目录一、基于比较的排序算法概述1.插入排序(InsertionSort)2.选择排序(SelectionSort)3.冒泡排序(BubbleSort)4.归并排序(MergeSort)5.快速排序(QuickSort)6.堆排序(HeapSort)7.希尔排序(ShellSort)二、排序算法的性能分析三、Java中的常用排序方法 在计算机科......
  • Java二叉树经典进阶OJ题解
     目录一、判断一颗二叉树是否为对称二叉树1.题目描述:2.代码示例:3.通过演示与分析:二、根据先序遍历结果构造二叉树1.题目描述:2.代码示例:3.通过演示与分析:三、层序遍历的非递归实现1.题目描述:2.代码示例:3.通过演示与分析:四、判断是否为完全二叉树1.题目描述:2.......
  • C++ 插入排序
    【预告】        这几次将讲讲排序(从简单开始),废话不多说,直接切入正题【关于插入排序】【定义】    定义:插入排序是一种简单直观的排序算法,它的工作原理是通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入【时间复杂度】 ......
  • 前端必修技能:高手进阶核心知识分享 - CSS 选择器
    前端必修技能:高手进阶核心知识分享-CSS选择器CSS(层叠样式表)提供了多种选择器,用于选择要应用样式的HTML元素。CSS选择器用于选择你想要的元素的样式的模式。看了上面的图,你发现就算你不知道选择器名字叫什么,属于哪一种,但不知不觉的,你其实已经习惯了其中的很多种选择器......
  • 归并排序
    时间复杂度\(o(nlogn)\)空间复杂度\(o(n)\)模板:#include<bits/stdc++.h>usingnamespacestd;constintN=1e5+10;intn;inta[N],b[N];//合并voidmerge(intleft,intright,intmid){ //标记左右半区第一个未排序的元素 intl_pos=left,r_pos=......
  • 如何让 Mypy 认识到对两个整数进行排序会返回两个整数
    我的代码如下:fromtypingimportTuplea:Tuple[int,int]=tuple(sorted([1,3]))Mypy告诉我:赋值中的不兼容类型(表达式的类型为“Tuple[int,...]”,变量类型为“Tuple[int,int]”)我做错了什么?为什么Mypy无法弄清楚排序后的元组将返回两个整数?Mypy......
  • QListWidget实现内部拖动排序功能
    1.需求将QListWidget有很多的任务,需要拖动任务,手动进行排序,并且需要保存拖动之后的顺序,软件重新打开要是修改之后的顺序;(1)继承QListWidget,重写一个QListWidget自定义类#ifndefDROPLISTWIDGET_H#defineDROPLISTWIDGET_H#include<QListWidget>#include<QDropEvent>clas......
  • 代码随想录算法训练营第40天 | 完全背包问题:完全背包基础理论、518.零钱兑换II、377.
    完全背包基础理论https://programmercarl.com/背包问题理论基础完全背包.html#其他语言版本卡码网完全背包例题https://kamacoder.com/problempage.php?pid=1052518.零钱兑换IIhttps://leetcode.cn/problems/coin-change-ii/description/代码随想录https://programmercarl......
  • C++进阶 继承
    目录继承的概念及定义继承概念继承定义定义格式 继承关系和访问限定符 继承基类成员访问方式的变化基类和派生类对象赋值转换继承中的作用域派生类的默认成员函数构造函数 拷贝构造函数 赋值运算符重载析构函数总结继承与友元继承与静态成员浅谈复杂的菱......
  • C++题目:DNA排序 代码
    题目描述现在有一些长度相等的 ......