
一、问题引入(痛点场景)
真题溯源:洛谷P1104(普及-难度)
用户痛点:
"如何实现年龄从大到小排序?同生日时如何保证输入靠后的同学先输出?多关键字排序的稳定性怎么处理?"
这是典型的多关键字排序问题,在CSP-J/S竞赛中频繁出现。题目要求按年龄从大到小排序,同生日时保持输入顺序,考查选手对排序算法稳定性的理解。
竞赛价值:此类排序问题在近年竞赛中出现频率极高,占分约10-15分,是必须掌握的基础题型。
二、核心算法分析
2.1 问题本质:稳定多关键字排序
关键洞察:
- 主要关键字:出生日期(年→月→日),数值越小年龄越大
- 次要关键字:输入顺序,同生日时后输入的先输出
- 需要自定义排序规则

2.2 排序规则设计
年龄比较逻辑:
- 年份比较:年份越小,年龄越大
- 月份比较:同年时,月份越小,年龄越大
- 日期比较:同年同月时,日期越小,年龄越大
- 输入顺序:同生日时,后输入的先输出
三、代码实现详解
3.1 标准解法(结构体+稳定排序)
#include <iostream>
#include <algorithm>
#include <vector>
#include <string>
using namespace std;
// 定义学生结构体
struct Student {
string name;
int year, month, day;
int index; // 记录输入顺序
};
// 自定义排序规则
bool compare(const Student &a, const Student &b) {
// 首先按出生日期排序(年份越小年龄越大)
if (a.year != b.year) {
return a.year < b.year; // 年份小的在前(年龄大)
}
if (a.month != b.month) {
return a.month < b.month; // 月份小的在前
}
if (a.day != b.day) {
return a.day < b.day; // 日期小的在前
}
// 同生日时,输入顺序靠后的先输出(索引大的在前)
return a.index > b.index;
}
int main() {
int n;
cin >> n;
vector<Student> students(n);
// 读取学生数据,记录输入顺序
for (int i = 0; i < n; i++) {
cin >> students[i].name >> students[i].year >> students[i].month >> students[i].day;
students[i].index = i; // 记录输入顺序(从0开始)
}
// 使用稳定排序保证同生日时的顺序
stable_sort(students.begin(), students.end(), compare);
// 输出结果
for (const auto &student : students) {
cout << student.name << endl;
}
return 0;
}
3.2 优化解法(Lambda表达式)
#include <iostream>
#include <algorithm>
#include <vector>
#include <string>
using namespace std;
struct Student {
string name;
int year, month, day;
int index;
};
int main() {
int n;
cin >> n;
vector<Student> students(n);
for (int i = 0; i < n; i++) {
cin >> students[i].name >> students[i].year >> students[i].month >> students[i].day;
students[i].index = i;
}
// 使用Lambda表达式简化代码
sort(students.begin(), students.end(), [](const Student &a, const Student &b) {
if (a.year != b.year) return a.year < b.year;
if (a.month != b.month) return a.month < b.month;
if (a.day != b.day) return a.day < b.day;
return a.index > b.index; // 同生日时后输入的先输出
});
for (const auto &student : students) {
cout << student.name << endl;
}
return 0;
}
四、算法原理深度解析
4.1 多关键字排序原理
排序优先级:
- 第一优先级:年份(升序)→ 年份小年龄大
- 第二优先级:月份(升序)→ 月份小年龄大
- 第三优先级:日期(升序)→ 日期小年龄大
- 第四优先级:输入顺序(降序)→ 后输入先输出
4.2 稳定排序的重要性
为什么使用stable_sort:
// 普通sort可能破坏同生日元素的相对顺序
sort(students.begin(), students.end(), compare);
// stable_sort保证同生日时输入顺序不变
stable_sort(students.begin(), students.end(), compare);
五、避坑指南与调试技巧
5.1 常见错误分析
错误1:年龄比较逻辑错误
// 错误:直接按日期数值大小比较
return a.year < b.year && a.month < b.month && a.day < b.day;
// 正确:分层比较
if (a.year != b.year) return a.year < b.year;
if (a.month != b.month) return a.month < b.month;
return a.day < b.day;
错误2:输入顺序处理错误
// 错误:同生日时按输入顺序升序排列
return a.index < b.index; // 应该让后输入的在前
// 正确:同生日时输入顺序大的在前
return a.index > b.index;
5.2 测试用例设计
void testCases() {
// 用例1:题目样例
// 输入:3个学生,验证排序正确性
// 用例2:同生日测试
// 输入:两个同生日的学生,验证输入顺序处理
// 用例3:边界测试(n=2)
// 输入:最小有效数据量
// 用例4:极端日期测试
// 输入:1960年和2020年的边界情况
}
六、性能优化建议
6.1 数据范围分析
题目数据保证:
- 1 < n < 100(数据量很小)
- 使用O(n²)的排序算法也可接受
- 重点在于排序规则的正确性
6.2 代码简洁性优化
使用tuple简化比较:
bool compare(const Student &a, const Student &b) {
return make_tuple(a.year, a.month, a.day, -a.index) <
make_tuple(b.year, b.month, b.day, -b.index);
}
七、竞赛应用与扩展
7.1 同类题型推荐
- 洛谷P1068:分数线划定(类似多关键字排序)
- 洛谷P1093:奖学金问题(结构体排序应用)
- 洛谷P1781:总统选举(排序规则设计)
7.2 算法思维拓展
从生日排序到更复杂问题:
- 掌握多关键字排序的通用模式
- 理解稳定排序的应用场景
- 学会设计复杂的比较规则
竞赛技巧提升:
- 熟练使用结构体存储复杂数据
- 掌握Lambda表达式的简洁写法
- 注意排序规则的边界情况处理
🔥 关注我,解锁CSP-J/S竞赛全攻略 🔥
(每日更新高频考点 + 精选真题解析,助你轻松备赛!)
👇 点击关注 → 立即提升竞赛战力 👇
[https://blog.csdn.net/stillwatersss]
📚 专栏亮点抢先看
-
高频考点突破
- 每日题解:精选洛谷/LeetCode CSP-J/S经典真题,附详细题解与时间复杂度优化技巧
- 考点拆解:动态规划、图论、字符串算法等核心专题深度剖析,直击竞赛命题规律
- 实战模板:限时领取《C++竞赛模板大全》👉 关注后私信回复“模板”获取
-
备赛效率翻倍技巧
- 从O(n²)到O(n):独家算法优化套路,解决TLE超时问题
- 考场避坑指南:常见失分点分析 + 数据边界处理技巧
- 互动答疑:评论区留言题目编号,优先解析你的个性化难题
-
独家福利🌟
- 粉丝专享:高价值文章设为 “仅粉丝可见”(如《CSP-J/S近5年考点分布与预测》)
- 资料包:关注后私信 “资料” 领取 竞赛真题库+调试代码工具包
💡 为什么值得关注?
✅ 数据驱动:内容基于CSP-J/S真题大数据,命中率超80%
✅ 即学即用:每篇附可运行代码(代码通过洛谷测评)与测试用例
✅ 垂直领域:专注竞赛辅导,拒绝泛技术水文,直击备赛痛点
📢 今日关注福利:前100名新粉丝回复【进阶】赠送《洛谷青铜~黄金段位进阶题库》📘
🔥 行动提示:点击主页 → 专栏 → 开启订阅更新,系统自动推送最新解析!
&spm=1001.2101.3001.5002&articleId=153649915&d=1&t=3&u=416a5f99126146409bdeb7f8f56228b1)
1066

被折叠的 条评论
为什么被折叠?



