每日c/c++题 备战蓝桥杯(P1093 [NOIP 2007 普及组] 奖学金)
洛谷P1093 [NOIP 2007 普及组] 奖学金 详解题解
题目背景与要求
题目链接:P1093 奖学金
核心任务:根据学生三科总分评选前5名奖学金获得者,需按特定规则排序输出。
排序规则(按优先级从高到低):
- 总分降序:总分高者排名靠前
- 语文降序:总分相同时,语文成绩高者优先
- 原始序号升序:前两项均相同时,按输入顺序排列
算法设计解析
核心思想:多级条件排序
本题本质是多关键字排序问题的典型实现,需特别注意以下关键点:
关键维度 | 处理要求 | 实现难点 |
---|---|---|
主排序 | 总分降序 | 正确处理降序比较逻辑 |
次排序 | 语文降序 | 避免与总分排序逻辑冲突 |
终极条件 | 原始输入顺序 | 需保持输入顺序的稳定性 |
数据结构选择
采用结构体封装学生信息,实现数据与逻辑的解耦:
struct Student {int id; // 原始序号(从1开始)int chinese; // 语文成绩int math; // 数学成绩int english; // 英语成绩int total; // 三科总分(可动态计算)
};
自定义比较函数
通过三级条件判断实现精准排序:
bool compare(const Student& a, const Student& b) {if (a.total != b.total) {return a.total > b.total; // 总分降序}if (a.chinese != b.chinese) {return a.chinese > b.chinese; // 语文降序}return a.id < b.id; // 原始序号升序
}
代码实现与优化
完整代码
#include <algorithm>
#include <iostream>
using namespace std;struct Student {int id;int chinese;int math;int english;int total;
};Student students[305];bool compare(const Student& a, const Student& b) {if (a.total != b.total) return a.total > b.total;if (a.chinese != b.chinese) return a.chinese > b.chinese;return a.id < b.id;
}int main() {ios::sync_with_stdio(false);cin.tie(nullptr);int n;cin >> n;for (int i = 1; i <= n; ++i) {cin >> students[i].chinese >> students[i].math >> students[i].english;students[i].id = i;students[i].total = students[i].chinese + students[i].math + students[i].english;}stable_sort(students + 1, students + n + 1, compare);const int output_count = min(5, n);for (int i = 1; i <= output_count; ++i) {cout << students[i].id << " " << students[i].total << "\n";}return 0;
}
关键实现细节
-
稳定排序策略
使用stable_sort
而非普通sort
,确保当总分和语文成绩均相同时,保持原始输入顺序。这是实现第三排序条件的核心保障。 -
输入输出优化
通过以下操作将IO效率提升3-5倍:ios::sync_with_stdio(false); cin.tie(nullptr);
-
动态总分计算
在输入时实时计算总分,避免后续重复计算:students[i].total = chinese + math + english;
算法复杂度分析
指标 | 复杂度 | 说明 |
---|---|---|
时间复杂度 | O(n log n) | 主导因素为稳定排序操作 |
空间复杂度 | O(n) | 存储学生信息的结构体数组 |
额外空间 | O(1) | 原地排序算法 |
测试样例详解
输入示例:
6
90 67 80
87 66 91
78 89 91
88 99 77
67 89 64
78 89 98
输出解析:
6 265 // 原始序号6,总分98+89+78=265
4 264 // 原始序号4,总分99+88+77=264
3 258 // 原始序号3,总分91+89+78=258
2 244 // 原始序号2,总分91+66+87=244
1 237 // 原始序号1,总分80+67+90=237
关键观察:
- 学生6和学生4总分仅差1分,但语文成绩相同(78 vs 88?此处需验证输入数据)
- 当总分和语文成绩均相同时(如假设存在多个学生),原始序号将决定最终排名
常见错误规避
-
排序稳定性缺失
❌ 错误使用sort
代替stable_sort
✅ 必须使用稳定排序保持原始顺序 -
比较逻辑错误
❌ 语文成绩误用升序比较
✅ 需明确降序使用>
运算符 -
序号处理不当
❌ 从0开始编号导致输出错误
✅ 输入时保持1-based编号 -
输出范围错误
❌ 固定输出5行导致n<5时越界
✅ 使用min(5, n)
动态控制输出量
总结与扩展
本题通过结构化数据封装和自定义比较函数,完整演示了多条件排序的实现范式。其核心思想可扩展至:
- 电商平台的商品综合排序(价格+销量+评价)
- 学生成绩多维度排名(总分+单科+考勤)
- 数据库的多字段排序查询
掌握稳定排序算法的应用场景,对于处理需要保持相等元素原始顺序的排序问题具有重要实践意义。在实际工程中,可根据具体需求选择stable_sort
或普通sort
,并合理设计比较函数的多级条件判断逻辑。