news 2026/6/10 0:39:54

NOIP2009普及组真题解析:用C++的sort函数搞定‘分数线划定’,附四种解法对比

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
NOIP2009普及组真题解析:用C++的sort函数搞定‘分数线划定’,附四种解法对比

NOIP2009普及组真题解析:用C++的sort函数搞定‘分数线划定’,附四种解法对比

在信息学奥赛的备战过程中,排序算法是每位选手必须掌握的核心技能。2009年NOIP普及组的"分数线划定"题目,不仅考察了基础的排序能力,更考验选手对不同排序算法特性的理解。这道题的数据规模虽然不大(最多5000条记录),但恰恰适合用来深入探讨各种排序方法的优劣。

本文将带你从零开始拆解这道经典题目,重点分析C++标准库中sort函数的灵活应用,同时对比结构体排序、冒泡排序、计数排序+插入排序、stable_sort稳定排序四种实现方案。不同于简单的AC题解,我们会深入探讨每种算法的时间复杂度、适用场景和编码技巧,帮助你在竞赛中根据数据特征快速选择最优解法。

1. 题目分析与解题思路

"分数线划定"的题目要求可以简化为:给定n个考生的报名号和成绩,按照成绩从高到低排序,成绩相同时按报名号从小到大排序。然后确定录取分数线为排名第m*1.5(向下取整)的考生成绩,最后输出所有不低于该分数线的考生信息。

关键解题步骤

  1. 数据输入:存储每个考生的报名号和成绩
  2. 自定义排序
    • 主排序键:成绩降序
    • 次排序键:报名号升序
  3. 分数线计算:取第⌊m*1.5⌋个考生的成绩
  4. 结果输出:统计并输出所有不低于分数线的考生

注意:题目中的m1.5需要向下取整,C++中可以直接使用int(m1.5)实现。

这道题的数据规模n≤5000,意味着O(n²)的算法(如冒泡排序)也能在规定时间内完成。但在实际竞赛中,养成使用高效算法的习惯非常重要,因为题目数据范围可能会在后续年份调整。

2. 结构体排序:最清晰的解法

使用结构体存储考生信息,配合自定义比较函数,是最直观的解决方案。这种方法代码可读性强,易于维护,是竞赛中的首选方案。

#include <bits/stdc++.h> using namespace std; #define N 5005 struct Student { int id, score; }; bool compare(const Student &a, const Student &b) { if(a.score != b.score) return a.score > b.score; // 成绩降序 return a.id < b.id; // 报名号升序 } int main() { Student stu[N]; int n, m; cin >> n >> m; for(int i = 0; i < n; ++i) cin >> stu[i].id >> stu[i].score; sort(stu, stu + n, compare); int cutoff = stu[int(m * 1.5)].score; int count = 0; while(count < n && stu[count].score >= cutoff) count++; cout << cutoff << " " << count << endl; for(int i = 0; i < count; ++i) cout << stu[i].id << " " << stu[i].score << endl; return 0; }

性能分析

  • 时间复杂度:O(n log n),来自sort函数
  • 空间复杂度:O(n),存储所有考生信息
  • 优点:代码简洁,逻辑清晰,适合各种规模的数据
  • 缺点:需要额外定义结构体和比较函数

3. 冒泡排序:理解排序本质

虽然在实际竞赛中不推荐使用冒泡排序,但通过实现它可以帮助我们深入理解排序算法的基本原理。下面是使用冒泡排序的解法:

#include <iostream> using namespace std; #define N 5005 int main() { int ids[N], scores[N]; int n, m; cin >> n >> m; for(int i = 0; i < n; ++i) cin >> ids[i] >> scores[i]; // 冒泡排序实现 for(int i = 0; i < n-1; ++i) { for(int j = 0; j < n-i-1; ++j) { if(scores[j] < scores[j+1] || (scores[j] == scores[j+1] && ids[j] > ids[j+1])) { swap(scores[j], scores[j+1]); swap(ids[j], ids[j+1]); } } } int cutoff = scores[int(m * 1.5)]; int count = 0; while(count < n && scores[count] >= cutoff) count++; cout << cutoff << " " << count << endl; for(int i = 0; i < count; ++i) cout << ids[i] << " " << scores[i] << endl; return 0; }

性能分析

  • 时间复杂度:O(n²),对于n=5000,循环次数约为2500万次
  • 空间复杂度:O(n)
  • 优点:不依赖STL,有助于理解排序原理
  • 缺点:效率低,在大数据量时可能超时

4. 计数排序+插入排序:特定场景的优化

当成绩范围有限时(如本题中成绩≤100),可以采用计数排序+插入排序的混合策略。这种方法在成绩分布集中的情况下效率极高。

#include <iostream> #include <vector> using namespace std; vector<int> scoreBuckets[101]; // 0-100分 int main() { int n, m; cin >> n >> m; for(int i = 0; i < n; ++i) { int id, score; cin >> id >> score; // 使用插入排序保持每个桶内id有序 auto &bucket = scoreBuckets[score]; auto it = bucket.begin(); while(it != bucket.end() && *it < id) ++it; bucket.insert(it, id); } int cutoff = 0, count = 0; for(int s = 100; s >= 0; --s) { count += scoreBuckets[s].size(); if(count >= int(m * 1.5)) { cutoff = s; break; } } // 重新计算精确的count count = 0; for(int s = 100; s >= cutoff; --s) count += scoreBuckets[s].size(); cout << cutoff << " " << count << endl; for(int s = 100; s >= cutoff; --s) for(int id : scoreBuckets[s]) cout << id << " " << s << endl; return 0; }

性能分析

  • 时间复杂度:O(n + k),其中k是成绩范围(101)
  • 空间复杂度:O(n + k)
  • 优点:当k<<n时,效率极高
  • 缺点:成绩范围大时空间消耗大,且实现较复杂

5. stable_sort两阶段排序:保持稳定性的方案

如果需要保持排序稳定性(即相同键值的元素保持原有顺序),可以使用stable_sort进行两阶段排序:

#include <bits/stdc++.h> using namespace std; #define N 5005 struct Student { int id, score; }; bool compareId(const Student &a, const Student &b) { return a.id < b.id; } bool compareScore(const Student &a, const Student &b) { return a.score > b.score; } int main() { Student stu[N]; int n, m; cin >> n >> m; for(int i = 0; i < n; ++i) cin >> stu[i].id >> stu[i].score; // 先按id排序,保证相同score时id小的在前 stable_sort(stu, stu + n, compareId); // 再按score排序,保持相同score元素的相对顺序 stable_sort(stu, stu + n, compareScore); int cutoff = stu[int(m * 1.5)].score; int count = 0; while(count < n && stu[count].score >= cutoff) count++; cout << cutoff << " " << count << endl; for(int i = 0; i < count; ++i) cout << stu[i].id << " " << stu[i].score << endl; return 0; }

性能分析

  • 时间复杂度:O(n log n),但常数因子比普通sort大
  • 空间复杂度:O(n)
  • 优点:保持排序稳定性,适合需要保持相对顺序的场景
  • 缺点:需要两次排序,效率略低

6. 四种解法对比与选择策略

为了帮助大家在实际竞赛中快速选择合适的方法,我们总结四种解法的特点:

解法时间复杂度空间复杂度编码复杂度适用场景
结构体+sortO(n log n)O(n)通用场景,推荐首选
冒泡排序O(n²)O(n)教学用途,实际竞赛不推荐
计数+插入O(n + k)O(n + k)成绩范围小(k小)时高效
stable_sortO(n log n)O(n)需要保持稳定性的场景

在实际比赛中,建议优先考虑结构体+sort的方案,它提供了最佳的平衡点:代码简洁、效率高、可读性好。只有当题目有特殊要求(如明确需要稳定性或数据范围特殊)时,才考虑其他方案。

选择排序算法的几个考量因素

  1. 数据规模:n的大小直接影响算法选择
  2. 数据特性:是否部分有序、键值范围等
  3. 稳定性要求:是否需要保持相同键值的相对顺序
  4. 空间限制:是否有严格的内存限制
  5. 编码时间:竞赛中时间宝贵,选择实现快速的方案

在NOIP/CSP等竞赛中,掌握sort函数的灵活应用可以解决80%以上的排序需求。建议重点练习结构体排序方法,并理解比较函数的编写规则。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/10 0:39:21

多层防御体系搭建:从边缘到源站全域安全防护指南

多层防御体系架构设计边缘层采用WAF&#xff08;Web应用防火墙&#xff09;和DDoS防护&#xff0c;过滤恶意流量并缓解分布式拒绝服务攻击。部署CDN节点缓存静态内容&#xff0c;减少源站暴露风险。网络层实施零信任模型&#xff0c;基于身份的动态访问控制替代传统边界防御。使…

作者头像 李华
网站建设 2026/6/10 0:32:03

Python+Django实战|美食菜谱分享与食材采购一体化系统:食谱发布收藏、图文教程、食材商城、购物车、订单管理、美食点评、智能食谱推荐

一、项目背景与痛点 当下美食爱好者、家庭主厨、餐饮从业者获取菜谱、采购食材的需求十分普遍。主流美食平台大多广告繁杂、教程碎片化&#xff0c;同时菜谱与食材相互割裂&#xff0c;存在诸多现实痛点&#xff1a; 菜谱资源杂乱无章&#xff1a;各类食谱分散在不同平台、短视…

作者头像 李华
网站建设 2026/6/10 0:25:52

深度解析:douyin-downloader抖音音频提取与批量下载实战指南

深度解析&#xff1a;douyin-downloader抖音音频提取与批量下载实战指南 【免费下载链接】douyin-downloader A practical Douyin downloader for both single-item and profile batch downloads, with progress display, retries, SQLite deduplication, and browser fallback…

作者头像 李华
网站建设 2026/6/10 0:17:30

Java DFA算法

DFA&#xff08;Deterministic Finite Automaton&#xff0c;确定性有限自动机&#xff09;是一种常用的算法模型&#xff0c;在Java中广泛应用于敏感词过滤、字符串匹配、词法分析等场景。它的核心特点是&#xff1a;每个状态对于同一个输入字符&#xff0c;有且只有一个转移状…

作者头像 李华
网站建设 2026/6/10 0:16:48

当SingleR不给力时:手把手教你用Seurat和文献Marker基因手动注释细胞类型

当SingleR失效时&#xff1a;基于Seurat与文献Marker基因的细胞类型精准注释指南生物信息学分析中&#xff0c;单细胞RNA测序数据的细胞类型注释是理解组织异质性的关键步骤。虽然SingleR等自动注释工具为研究者提供了便利&#xff0c;但在实际应用中常遇到注释模糊、跨物种匹配…

作者头像 李华