CSP-J 2019 入门级 第一轮(初赛) 完善程序(2)

【题目】

CSP-J 2019 入门级 第一轮(初赛) 完善程序(2)
(计数排序)计数排序是一个广泛使用的排序方法。下面的程序使用双关键字计数排序,将n对10000 以内的整数,从小到大排序。
例如有三对整数 (3,4)、(2,4)、(3,3),那么排序之后应该是(2,4)、(3,3)、(3,4) 。
输入第一行为n,接下来n行,第i行有两个数a[i]和b[i],分别表示第i对整数的第一关键字和第二关键字。
从小到大排序后输出。
数据范围 1 < n < 1 0 7 1<n<10^7 1<n<107 1 < a [ i ] , b [ i ] < 1 0 4 1<a[i],b[i]<10^4 1<a[i],b[i]<104
提示:应先对第二关键字排序,再对第一关键字排序。数组 ord[] 存储第二关键字排序的结果,数组 res[] 存储双关键字排序的结果。

试补全程序。

#include <cstdio>
#include <cstring>
using namespace std;
const int maxn = 10000000;
const int maxs = 10000;int n;
unsigned a[maxn], b[maxn],res[maxn], ord[maxn];
unsigned cnt[maxs + 1];
int main() {scanf("%d", &n);for (int i = 0; i < n; ++i) scanf("%d%d", &a[i], &b[i]);memset(cnt, 0, sizeof(cnt));for (int i = 0; i < n; ++i); // 利用 cnt 数组统计数量for (int i = 0; i < maxs; ++i) cnt[i + 1] += cnt[i];for (int i = 0; i < n; ++i); // 记录初步排序结果memset(cnt, 0, sizeof(cnt));for (int i = 0; i < n; ++i); // 利用 cnt 数组统计数量for (int i = 0; i < maxs; ++i)cnt[i + 1] += cnt[i];for (int i = n - 1; i >= 0; --i)// 记录最终排序结果for (int i = 0; i < n; i++)printf("%d %d",);return 0;
}
  1. ①处应填()
    A. ++cnt[i]
    B. ++cnt[b[i]]
    C. ++cnt[a[i] * maxs + b[i]]
    D. ++cnt[a[i]]
  2. ②处应填()
    A. ord[–cnt[a[i]]] = i
    B. ord[–cnt[b[i]]] = a[i]
    C. ord[–cnt[a[i]]] = b[i]
    D. ord[–cnt[b[i]]] = i
  3. ③处应填()
    A. ++cnt[b[i]]
    B. ++cnt[a[i] * maxs + b[i]]
    C. ++cnt[a[i]]
    D. ++cnt[i]
  4. ④处应填()
    A. res[–cnt[a[ord[i]]]] = ord[i]
    B. res[–cnt[b[ord[i]]]] = ord[i]
    C. res[–cnt[b[i]]] = ord[i]
    D. res[–cnt[a[i]]] = ord[i]
  5. ⑤处应填()
    A. a[i], b[i]
    B. a[res[i]], b[res[i]]
    C. a[ord[res[i]]],b[ord[res[i]]]
    D. a[res[ord[i]]],b[res[ord[i]]]

【题目考点】

1. 索引排序
2. 计数排序
3. 排序的稳定性
4. 前缀和

【解题思路】

对数对进行排序,目标顺序为:先按第一个数字从小到大排序,如果第一个数字相同,再按第二数字从小到大排序。
如果使用稳定的排序算法,则可以先按第二个数字从小到大对所有数对排序,此时数对序列的顺序已经满足第二个数字是升序的。再按照第一个数字从小到大排序,如果第一个数字相同,由于使用的是稳定的排序算法,数对的第二个数字会按照其原有的升序顺序排列,最终结果就符合了目标顺序。
本题使用的是计数排序。

#include <cstdio>
#include <cstring>
using namespace std;
const int maxn = 10000000;
const int maxs = 10000;int n;
unsigned a[maxn], b[maxn],res[maxn], ord[maxn];
unsigned cnt[maxs + 1];

题目说了,有n对10000以内的整数,代码中常量maxs是10000,以maxs为长度的数组cnt应该为计数数组,cnt[i]表示数值i出现的次数。

int main() {scanf("%d", &n);for (int i = 0; i < n; ++i) scanf("%d%d", &a[i], &b[i]);memset(cnt, 0, sizeof(cnt));for (int i = 0; i < n; ++i); // 利用 cnt 数组统计数量

输入每个数对 ( a i , b i ) (a_i,b_i) (ai,bi),先将计数数组cnt每个元素初始化为0。而后要做的就是“利用 cnt 数组统计数量”。题目的提示中说了:“应先对第二关键字排序,再对第一关键字排序”,也就是应该先根据第二个关键字从小到大对各数对进行排序。先对第二个关键字进行计数,此时cnt[b[i]]表示第二个关键字值为b[i]的数对的数量。第i个数对为 ( a i , b i ) (a_i,b_i) (ai,bi),那么第二个关键字为b[i]的数对的数量应该增加1,即cnt[b[i]]++第(1)空填cnt[b[i]]++,选B

	for (int i = 0; i < maxs; ++i) cnt[i + 1] += cnt[i];for (int i = 0; i < n; ++i); // 记录初步排序结果

遍历执行cnt[i+1] += cnt[i],cnt数组经过更新后变成了自己的前缀和。
cnt[b[i]]的概念变为第二个关键字小于等于b[i]的数对的数量,其中最后一个数对就是从1开始数的第cnt[b[i]]个数对,也就是从0开始数的第cnt[b[i]]-1个数对。
假想按照输入顺序得到原数对序列为 s s s,排序后的目标数对序列为 t t t,由于原序列 s s s和目标序列 t t t都是下标从0开始的,此时cnt[b[i]]-1也就是所有第二个关键字小于等于b[i]的数对按照第二个关键字升序排序后最后一个数对的下标,也就是第二个关键字等于b[i]的最后一个数对的下标。
目标序列 t t t的第i个(下标i位置)的元素 t [ i ] t[i] t[i]在原序列中 s s s的下标为ord[i],也就是满足 t [ i ] = s [ o r d [ i ] ] t[i] = s[ord[i]] t[i]=s[ord[i]],ord数组即为索引数组。
看原序列 s s s的第i数对 s [ i ] s[i] s[i],其第二个关键字为b[i]。第二个关键字为b[i]的所有数对在目标序列中最后一个数对的下标为cnt[b[i]]-1,先让cnt[b[i]]减少1,即--cnt[b[i]]。将当前第i数对 s [ i ] s[i] s[i]放在目标序列 t t t下标cnt[b[i]]位置,也就是 t [ c n t [ b [ i ] ] ] = s [ i ] t[cnt[b[i]]]=s[i] t[cnt[b[i]]]=s[i],那么目标序列第cnt[b[i]]元素在原序列中的下标为i,因此设ord[cnt[b[i]]] = i
cnt[b[i]]减少1后,下一个第二个关键字为 b i b_i bi的数对在目标序列中的最后一个元素的下标就是cnt[b[i]]-1。可以重复上述过程,求出该数对在排序后的下标。因此第(2)空填ord[--cnt[b[i]]] = i,选D

看一个使用上述过程进行计数排序的例子:原序列a为1 2 1 2 1
遍历计数,得cnt[1]:3, cnt[2]:2
cnt变为自己的前缀和后,cnt[1]:3, cnt[2]:5
排序后的目标序列为t序列,ord[i]t[i]在a序列中的下标。
遍历a序列,a[0]为1,最后一个1应该放在cnt[1]-1=2位置,所以先--cnt[1]cnt[1]变为2,而后t[cnt[1]]=1ord[cnt[a[0]]]=ord[2]=0。。
a[1]为2,最后一个2应该放在cnt[2]-1=4位置,所以先--cnt[2],而后t[cnt[2]]=2ord[cnt[a[1]]]=ord[4]=1
a[2]为1,最后一个1应该放在cnt[1]-1=1位置,所以先--cnt[1]cnt[1]变为1,而后t[cnt[1]]=1ord[cnt[a[2]]]=ord[1]=2
依此类推,填表后结果为:

下标01234
ta[4]:1a[2]:1a[0]:1a[3]:2a[1]:2
ord42031

i从0到n-1循环,重复上述过程,即可求出ord数组,得到了每个在目标序列中的数值在原序列中的下标,也就是求出了按照第二个关键字 b i b_i bi排序后的目标序列。

	memset(cnt, 0, sizeof(cnt));for (int i = 0; i < n; ++i); // 利用 cnt 数组统计数量for (int i = 0; i < maxs; ++i)cnt[i + 1] += cnt[i];

接下来要对根据第二关键字排序后的序列再根据第一关键字排序。
此时应该对a[i]进行计数,cnt[a[i]]此时表示a[i]出现的次数。第(3)空填++cnt[a[i]],选C。
而后又是cnt[i+1] += cnt[i]操作将cnt变为自己的前缀和。此时cnt[a[i]]为第一个关键字小于等于a[i]的数对的数量。序列下标从0开始,按第一个关键字排序后下标cnt[a[i]]-1位置就是第一个关键字为a[i]的最后一个数对的下标。

 	for (int i = n - 1; i >= 0; --i)// 记录最终排序结果for (int i = 0; i < n; i++)printf("%d %d",);return 0;
}

经过第一趟按照第二个关键字排序后得到数对序列 t t t,现在对 t t t序列进行遍历,按照第一个关键字进行计数排序,排序后的目标序列 u u u。目标序列 u u u中第i元素 u [ i ] u[i] u[i]在原序列 s s s中的下标为res[i],即 u [ i ] = s [ r e s [ i ] ] u[i] = s[res[i]] u[i]=s[res[i]],res也是索引数组。
其中 t [ i ] t[i] t[i]的第一个关键字记为 t [ i ] . a = s [ o r d [ i ] ] . a t[i].a=s[ord[i]].a t[i].a=s[ord[i]].a,就是a[ord[i]]。第二个关键字记为 t [ i ] . b = s [ o r d [ i ] ] . b t[i].b=s[ord[i]].b t[i].b=s[ord[i]].b,就是b[ord[i]]。
遍历 t t t序列,访问到第i个数对 t [ i ] t[i] t[i],其第一个关键字为 t [ i ] . a t[i].a t[i].a,第一个关键字为 t [ i ] . a t[i].a t[i].a的最后一个数对在目标序列中 u u u的下标为 c n t [ t [ i ] . a ] − 1 cnt[t[i].a]-1 cnt[t[i].a]1
先将 c n t [ t [ i ] . a ] cnt[t[i].a] cnt[t[i].a]减少1,而后可以设 u [ c n t [ t [ i ] . a ] ] = t [ i ] u[cnt[t[i].a]] = t[i] u[cnt[t[i].a]]=t[i]
已知 t [ i ] = s [ o r d [ i ] ] t[i] = s[ord[i]] t[i]=s[ord[i]],那么 u [ c n t [ t [ i ] . a ] ] = s [ o r d [ i ] ] u[cnt[t[i].a]]=s[ord[i]] u[cnt[t[i].a]]=s[ord[i]]
根据res的定义,有 r e s [ c n t [ t [ i ] . a ] ] = o r d [ i ] res[cnt[t[i].a]]=ord[i] res[cnt[t[i].a]]=ord[i]
已知 t [ i ] . a t[i].a t[i].a就是a[ord[i]],那么有res[cnt[a[ord[i]]]]=ord[i]
整合上面将 c n t [ t [ i ] . a ] cnt[t[i].a] cnt[t[i].a]减1的过程,实际需要执行的语句为res[--cnt[a[ord[i]]]] = ord[i],第(4)空选A
c n t [ t [ i ] . a ] cnt[t[i].a] cnt[t[i].a]减少1后,下一次遇到第一个关键字为 t [ i ] . a t[i].a t[i].a的数对,该数对应该在目标序列 u u u的下标 c n t [ t [ i ] . a ] − 1 cnt[t[i].a]-1 cnt[t[i].a]1位置,可以重复上述过程。
为了满足排序的稳定性,第一个关键字相同时第二个关键字从小到大排列。当前算法对于每个第一个关键字相同的数对,在目标序列中都是从后向前赋值的,因此需要按照第二个关键字从大到小的顺序遍历,也就是要对 t t t序列从后向前遍历,因此该循环的循环控制变量i是从n-1到0循环的。
最后输出的是最终序列 u u u u [ i ] = s [ r e s [ i ] ] u[i]=s[res[i]] u[i]=s[res[i]],因此输出的数对为a[res[i]],b[res[i]],第(5)空选B

【答案】

  1. B
  2. D
  3. C
  4. A
  5. B

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/75044.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Vue3 项目通过 docxtemplater 插件动态渲染 .docx 文档(带图片)预览,并导出

Vue3 项目通过 docxtemplater 插件动态渲染 .docx 文档&#xff08;带图片&#xff09;预览&#xff0c;并导出 预览安装插件示例代码项目目录结构截图实际效果截图 动态渲染 .docx 文档&#xff08;带图片&#xff09;&#xff0c;预览、导出安装插件docx 模板文件内容完整代码…

养老更安心!智绅科技“智慧”养老系统,智在何处?

在老龄化趋势不断加剧的当下&#xff0c;养老问题成为全社会关注的焦点。 人们对于养老服务的需求日益增长&#xff0c;不仅期望能够得到基本的生活照料&#xff0c;更渴望在安全、舒适、便捷的环境中安享晚年。 智绅科技的“智慧”养老系统应运而生&#xff0c;凭借其独特的…

MySQL 当中的锁

MySQL 当中的锁 文章目录 MySQL 当中的锁MySQL 中有哪些主要类型的锁&#xff1f;请简要说明MySQL 的全局锁有什么用&#xff1f;MySQL 的表级锁有哪些&#xff1f;作用是什么&#xff1f;元数据锁&#xff08;MetaData Lock&#xff0c;MDL&#xff09;意向锁&#xff08;Inte…

vue前端代码作业——待办事项

美化样式示意图&#xff1a; 后端IDEA代码示意图&#xff1a; 代码解释&#xff1a; 1. isAllChecked 计算属性的作用 isAllChecked 用于实现 “全选 / 全不选” 功能&#xff0c;它是一个 双向绑定 的计算属性&#xff08;因为 v-model 需要同时支持读取和设置值&#xff09…

Oracle数据库数据编程SQL<3.1 PL/SQL 匿名块 及 流程控制中的条件判断、循环、异常处理和随机函数应用>

PL/SQL部分 在SQL的基础上增加了一些过程化的控制语句。 过程化控制语句包括&#xff1a;类型定义、判断、循环、游标、异常处理&#xff08;例外处理&#xff09; 目录 PL/SQL匿名块 一、匿名块基本结构 1、匿名块由三个部分组成&#xff1a; 2、注意事项&#xff1a; …

DeepSeek详解:探索下一代语言模型

文章目录 前言一、什么是DeepSeek二、DeepSeek核心技术2.1 Transformer架构2.1.1 自注意力机制 (Self-Attention Mechanism)(a) 核心思想(b) 计算过程(c) 代码实现 2.1.2 多头注意力 (Multi-Head Attention)(a) 核心思想(b) 工作原理(c) 数学描述(d) 代码实现 2.1.3 位置编码 (…

Git Reset 命令详解与实用示例

文章目录 Git Reset 命令详解与实用示例git reset 主要选项git reset 示例1. 撤销最近一次提交&#xff08;但保留更改&#xff09;2. 撤销最近一次提交&#xff0c;并清除暂存区3. 彻底撤销提交&#xff0c;并丢弃所有更改4. 回退到特定的提交5. 取消暂存的文件 git reset 与 …

前端知识点---事件监听器里面的e.target跟this的区别,e.target在事件委托中的好处

文章目录 ✅ 相同点✅ 不同点✅ 总结区别e.target与事件委托之间的关系 在事件监听器中&#xff0c;e.target 和 this 有时是一样的&#xff0c;但它们并不完全相同。 ✅ 相同点 当事件直接绑定到元素时&#xff1a; e.target 和 this 通常指向相同的元素&#xff0c;即事件绑…

Elasticsearch 完全指南

1. Elasticsearch基础知识 1.1 什么是Elasticsearch Elasticsearch是一个基于Lucene的分布式、RESTful风格的搜索和数据分析引擎。它是一个开源的、高扩展的、分布式的全文搜索引擎,可以近乎实时地存储、检索数据。 Elasticsearch不仅仅是一个全文搜索引擎,它还可以用于以…

Python 3 与 MySQL 数据库连接:mysql-connector 模块详解

Python 3 与 MySQL 数据库连接&#xff1a;mysql-connector 模块详解 概述 在Python 3中&#xff0c;与MySQL数据库进行交互是一个常见的需求。mysql-connector是一个流行的Python模块&#xff0c;它提供了与MySQL数据库连接和交互的接口。本文将详细介绍mysql-connector模块…

SQL:CASE WHEN使用详解

文章目录 1. 数据转换与映射2. 动态条件筛选3. 多条件分组统计4. 数据排名与分级5. 处理空值与默认值6. 动态排序 CASE WHEN 语句在 SQL 中是一个非常强大且灵活的工具&#xff0c;除了常规的条件判断外&#xff0c;还有很多巧妙的用法&#xff0c;以下为你详细总结&#xff1a…

【字符设备驱动开发–IMX6ULL】(二)Linux 设备号

【字符设备驱动开发–IMX6ULL】&#xff08;二&#xff09;Linux 设备号 文章目录 【字符设备驱动开发–IMX6ULL】&#xff08;二&#xff09;Linux 设备号1 设备号的组成2.设备号的分配 1 设备号的组成 为了方便管理&#xff0c;Linux 中每个设备都有一个设备号&#xff0c;设…

【字符设备驱动开发–IMX6ULL】(一)简介

【字符设备驱动开发–IMX6ULL】&#xff08;一&#xff09;简介 一、Linux驱动与裸机开发区别 1.裸机驱动开发回顾 ​ 1、底层&#xff0c;跟寄存器打交道&#xff0c;有些MCU提供了库。 spi.c&#xff1a;主机驱动&#xff08;换成任何一个设备之后只需要调用此文件里面的…

YOLOv8+ Deepsort+Pyqt5车速检测系统

该系统通过YOLOv8进行高效的目标检测与分割&#xff0c;结合DeepSORT算法完成目标的实时跟踪&#xff0c;并利用GPU加速技术提升处理速度。系统支持模块化设计&#xff0c;可导入其他权重文件以适应不同场景需求&#xff0c;同时提供自定义配置选项&#xff0c;如显示标签和保存…

蓝桥杯嵌入式学习笔记

用博客来记录一下参加蓝桥杯嵌入式第十六届省赛的学习经历 工具环境准备cubemx配置外部高速时钟使能设置串口时钟配置项目配置 keil配置烧录方式注意代码规范头文件配置 模块ledcubemx配置keil代码实现点亮一只灯实现具体操作的灯&#xff0c;以及点亮还是熄灭 按键cubemx配置k…

ARCGIS PRO SDK VB2022 图层要素类类型判断

arcgis pro 常见要素类类型有以下几种&#xff1a; FeatureLayer ——要素图层&#xff08;矢量数据&#xff09; RasterLayer ——栅格图层 MapImageLayer ——地图图像图层 VectorTileLayer ——矢量切片图层 SceneLayer …

【hadoop】远程调试环境

根据上一节&#xff0c;我们已经安装完成hadoop伪分布式环境 hadoop集群环境配置_jdk1.8 441-CSDN博客 还没安装的小伙伴可以看看这个帖子 这一节我们要实现使用vscode进行远程连接&#xff0c;并且完成java配置与测试 目录 vscode 配置远程 安装java插件 新建java项目 …

Java版Manus实现来了,Spring AI Alibaba发布开源OpenManus实现

此次官方发布的 Spring AI Alibaba OpenManus 实现&#xff0c;包含完整的多智能体任务规划、思考与执行流程&#xff0c;可以让开发者体验 Java 版本的多智能体效果。它能够根据用户的问题进行分析&#xff0c;操作浏览器&#xff0c;执行代码等来完成复杂任务等。 项目源码及…

【Linux网络与网络编程】02.初识Socket编程

1. 数据传输的目的 前一篇文章中我们讲解了网络传输的流程&#xff0c;那么网络传输的目的是什么呢&#xff1f;难道我们只是将数据从一台主机传输到另一台主机吗&#xff1f; 当然不是的&#xff01;因为数据是给人用的。比如&#xff1a;聊天是人在聊天&#xff0c;下载是人…

电脑连不上手机热点会出现的小bug

一、问题展示 注意: 不要打开 隐藏热点 否则他就会在电脑上 找不到自己的热点 二、解决办法 把隐藏热点打开即可