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.pswp.cn/bicheng/75044.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/75044.shtml
英文地址,請注明出處:http://en.pswp.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

一、問題展示 注意: 不要打開 隱藏熱點 否則他就會在電腦上 找不到自己的熱點 二、解決辦法 把隱藏熱點打開即可