当前位置: 首页 > news >正文

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

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

在信息学竞赛的备考过程中,排序算法是最基础也是最重要的知识点之一。2009年NOIP普及组的"分数线划定"题目,正是考察选手对排序算法的理解和应用能力。这道题目看似简单,但其中蕴含的算法选择和优化思路,却值得我们深入探讨。

本文将围绕四种不同的解法展开分析,从结构体sort、冒泡排序到计数排序和stable_sort两趟排序,每种方法都有其独特的适用场景和优劣势。对于正在备战NOIP/CSP等竞赛的初中生或入门级选手来说,理解这些解法的差异并掌握在不同场景下的最佳选择策略,将大大提升解题效率和代码质量。

1. 题目分析与基础思路

"分数线划定"题目要求我们根据考生的报名号和成绩,按照特定规则进行排序后确定录取分数线。具体来说,需要先按成绩降序排序,成绩相同则按报名号升序排序,然后取第⌊m*1.5⌋个人的成绩作为分数线,最后输出所有不低于该分数线的考生信息。

题目给出的数据规模最大为5000,这意味着O(n²)时间复杂度的算法在理论上是可行的。但实际竞赛中,我们还需要考虑代码实现的简洁性、运行效率以及特殊比赛环境下的限制(如早期NOIP对STL的限制)。

核心解题步骤

  1. 输入考生信息(报名号和成绩)
  2. 按指定规则排序
  3. 确定分数线
  4. 统计并输出合格考生信息

2. 四种解法深度对比

2.1 结构体sort解法

这是最直观且现代C++推荐的解法,利用结构体组织数据,配合STL的sort函数实现自定义排序。

#include <bits/stdc++.h> using namespace std; #define N 5005 struct Stu { int k, s; // k:报名号 s:成绩 }; bool cmp(Stu &a, Stu &b) { if(a.s == b.s) return a.k < b.k; else return a.s > b.s; } int main() { Stu stu[N]; int n, m, line, ct = 0; cin >> n >> m; for(int i = 1; i <= n; ++i) cin >> stu[i].k >> stu[i].s; sort(stu+1, stu+1+n, cmp); line = stu[int(m*1.5)].s; for(int i = 1; i <= n; ++i) if(stu[i].s >= line) ct++; cout << line << ' ' << ct << endl; for(int i = 1; i <= ct; ++i) cout << stu[i].k << ' ' << stu[i].s << endl; return 0; }

优势分析

  • 时间复杂度:O(n log n),效率高
  • 代码简洁,逻辑清晰
  • 充分利用STL,减少手写代码量

适用场景

  • 现代NOIP/CSP竞赛(允许使用STL)
  • 需要快速实现且对性能有要求的场景

2.2 冒泡排序解法

这是一种传统的排序方法,不使用结构体,直接在数组上进行操作。

#include <bits/stdc++.h> using namespace std; #define N 5005 int main() { int k[N], s[N], n, m, line, ct = 0; cin >> n >> m; for(int i = 1; i <= n; ++i) cin >> k[i] >> s[i]; for(int i = 1; i <= n - 1; ++i) for(int j = 1; j <= n - i; ++j) if(s[j] < s[j+1] || s[j] == s[j+1] && k[j] > k[j+1]) { swap(s[j], s[j+1]); swap(k[j], k[j+1]); } line = s[int(m*1.5)]; for(int i = 1; i <= n; ++i) if(s[i] >= line) ct++; cout << line << ' ' << ct << endl; for(int i = 1; i <= ct; ++i) cout << k[i] << ' ' << s[i] << endl; return 0; }

性能对比

指标结构体sort冒泡排序
时间复杂度O(n log n)O(n²)
空间复杂度O(n)O(n)
代码复杂度
稳定性不稳定稳定

适用场景

  • 早期NOIP比赛(限制STL使用)
  • 教学演示排序算法原理
  • 数据规模非常小的情况

2.3 计数排序+插入排序解法

这种方法利用了成绩范围有限(0-100分)的特点,先按成绩分组,再对每组内的报名号进行排序。

#include <bits/stdc++.h> using namespace std; int score[105][5005] = {}; // score[i][0]存储该分数段人数 int main() { int k, s, n, m, line, ct = 0; cin >> n >> m; for(int i = 1; i <= n; ++i) { cin >> k >> s; score[s][++score[s][0]] = k; for(int j = score[s][0]; j > 1; --j) { if(score[s][j] < score[s][j-1]) swap(score[s][j], score[s][j-1]); else break; } } int lnum = int(m*1.5); for(int i = 100; i >= 0; --i) { ct += score[i][0]; if(ct >= lnum) { line = i; break; } } cout << line << ' ' << ct << endl; for(int i = 100; i >= line; --i) for(int j = 1; j <= score[i][0]; ++j) cout << score[i][j] << ' ' << i << endl; return 0; }

独特优势

  • 当成绩范围有限时,时间复杂度接近O(n)
  • 避免了通用排序算法的复杂度
  • 内存访问局部性好,实际运行效率可能很高

适用场景

  • 成绩范围明确且有限
  • 对特定数据分布有优化需求
  • 需要展示非比较排序算法的应用

2.4 stable_sort两趟排序解法

利用稳定排序的特性,先按次要关键字排序,再按主要关键字排序。

#include <bits/stdc++.h> using namespace std; #define N 5005 struct Stu { int k, s; }; bool cmp_k(const Stu &a, const Stu &b) { return a.k < b.k; } bool cmp_s(const Stu &a, const Stu &b) { return a.s > b.s; } int main() { Stu stu[N]; int n, m, line, ct = 0; cin >> n >> m; for(int i = 1; i <= n; ++i) cin >> stu[i].k >> stu[i].s; stable_sort(stu+1, stu+1+n, cmp_k); stable_sort(stu+1, stu+1+n, cmp_s); line = stu[int(m*1.5)].s; for(int i = 1; i <= n; ++i) if(stu[i].s >= line) ct++; cout << line << ' ' << ct << endl; for(int i = 1; i <= ct; ++i) cout << stu[i].k << ' ' << stu[i].s << endl; return 0; }

技术要点

  • 第一趟按报名号升序排序(次要关键字)
  • 第二趟按成绩降序排序(主要关键字)
  • 稳定排序保证相同成绩的考生保持报名号顺序

适用场景

  • 需要展示稳定排序特性的应用
  • 多关键字排序的教学示例
  • 对排序稳定性有要求的特殊场景

3. 竞赛实战中的选择策略

在实际竞赛环境中,选择哪种解法需要考虑多个因素:

1. 比赛规则限制

  • 现代NOIP/CSP:优先使用STL sort
  • 早期NOIP限制STL:冒泡排序或手写快排
  • 在线评测系统(如洛谷):选择最简洁高效的实现

2. 数据规模考量

  • n ≤ 5000:所有方法都可行
  • n ≤ 1000:冒泡排序也可接受
  • 成绩范围很小:计数排序优势明显

3. 编码效率与调试

  • 结构体sort:编码最快,调试最容易
  • 冒泡排序:需要小心边界条件
  • 计数排序:需要处理二维数组

4. 性能优化空间

  • 对于大规模数据,sort是最佳选择
  • 如果题目扩展为动态维护分数线,可能需要更复杂的数据结构

4. 常见错误与优化技巧

在实现这四种解法时,选手常会遇到一些典型问题:

常见错误

  1. 数组下标处理不当(特别是1-based和0-based混用)
  2. 多关键字排序时比较函数写错
  3. 分数线位置计算错误(注意整数除法)
  4. 输出时未正确处理同分考生

优化技巧

  1. 使用ios::sync_with_stdio(false)加速输入输出
  2. 对于固定范围数据,优先考虑非比较排序
  3. 合理使用结构体使代码更清晰
  4. 预先计算避免重复操作
// 输入输出优化示例 ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);

调试建议

  1. 先用小规模数据测试基本功能
  2. 检查边界情况(如所有考生同分)
  3. 验证排序结果的稳定性
  4. 对比不同解法的输出是否一致

在实际竞赛中,建议优先掌握结构体结合sort的解法,这是最通用且高效的方法。同时理解其他解法的思想,以便在特殊情况下能够灵活应变。对于初学者来说,手写冒泡排序也有助于深入理解排序算法的原理。

http://www.jsqmd.com/news/979992/

相关文章:

  • 2026年金属粉末粘合剂实力厂家,选购注意事项汇总
  • AI 推理网关设计:多模型路由与负载均衡策略,从单模型到智能调度
  • 2026分光光度计选购白皮书医疗机构科研定制指南:Mill200离子束刻蚀机、OpTest MTF传函仪、OptoCraft波前探测器选择指南 - 优质品牌商家
  • 重磅技术突破!六因子联合检测体系落地,云克隆Luminex平台赋能抗病毒免疫与炎症损伤的研究
  • 攀枝花市黄金回收本地靠谱店铺指南+白银回收+铂金回收+彩金回推荐收门店 及地联系方式址推荐 - 盛世金银回收
  • 别再纠结选哪个了!手把手教你用Qt和C#快速上手SCADA组态开发(附开源项目清单)
  • 别再死记硬背了!用这张Flink知识地图,带你从入门到实战(附学习路径)
  • 从手机快充到电动车:深入聊聊同步整流技术如何‘榨干’每一分效率
  • 深度解析feishu2md:专业级飞书文档到Markdown转换的技术实现方案
  • 日月不失其体,故蔽而复明;江汉不失其源,故穷而复通
  • 车辆CTRV运动建模下的C++无迹卡尔曼滤波工程实现(含雷达融合测试与可视化)
  • 文章标题:肇庆各区黄金回收哪家好 安全变现门店选择攻略 - 润富黄金回收
  • 告别云端排队!手把手教你用Mx-yolov3在本地电脑训练K210专属模型(附VOTT标注避坑指南)
  • 揭阳市黄金回收本地靠谱店铺指南+白银回收+铂金回收+彩金回推荐收门店 及地联系方式址推荐 - 盛世金银回收
  • FPGA开发用SPI模式0主从通信Verilog工程,含ModelSim可运行仿真环境
  • Java+Vue漫画阅读系统源码包:含部署教程、接口文档、数据库脚本与答辩PPT
  • 用Matlab手把手实现维特比译码(附完整代码与避坑指南)
  • 使用docker 部署向量数据库Milvus
  • 平顶山市黄金回收本地靠谱店铺指南+白银回收+铂金回收+彩金回推荐收门店 及地联系方式址推荐 - 盛世金银回收
  • Arduino 433MHz无线收发实战包:VirtualWire源码+DHT11传输示例+全文档
  • 从Copilot到Agent--我的开发工作流正在被颠覆
  • 金昌市黄金回收本地靠谱店铺指南+白银回收+铂金回收+彩金回推荐收门店 及地联系方式址推荐 - 盛世金银回收
  • 2025-2026年上海屋宁遮阳设备有限公司电话查询:选择遮阳产品前先了解服务范围 - 品牌推荐
  • 终极指南:3分钟掌握N_m3u8DL-CLI-SimpleG图形化下载工具
  • CVE-2026-43284 CVE-2026-43500 CVE-2026-46300 Dirty Frag 漏洞分析 --前车之鉴,后事之师
  • 从摘要到关键词:搞定论文‘门面’的完整流程与常见误区避坑(以计算机/材料学为例)
  • 平凉市黄金回收本地靠谱店铺指南+白银回收+铂金回收+彩金回推荐收门店 及地联系方式址推荐 - 盛世金银回收
  • Unlock Music音乐解锁工具:3分钟快速解密所有加密音乐格式
  • STM32F103用RS485跑Modbus RTU,直连中达优控HMI一体机的可调试工程
  • matchexpression和matchlabels的区别