PAT 甲级题目讲解:1012《The Best Rank》
✅ PAT 甲级题目讲解:1012《The Best Rank》
🧩 题目简介
本题要求根据每位学生的三门课程成绩(C, M, E)以及平均成绩 A,为每个指定学号输出其最佳排名和对应科目标识(A/C/M/E)。
若该学号不存在,则输出 N/A
。
🧪 样例分析
输入样例:
5 6
310101 98 85 88
310102 70 95 88
310103 82 87 94
310104 91 91 91
310105 85 90 90
310101
310102
310103
310104
310105
999999
- 第一行
5 6
表示有 5 个学生,6 次查询; - 接下来的 5 行每行一个学生,格式为:学号 C M E;
- 每名学生的平均成绩
A = (C + M + E) / 3.0
; - 要对所有学生在 A/C/M/E 四门科目分别排名;
- 查询每个学号时输出该生在四科中排名最靠前的名次及对应科目;
- 若学号不存在(如 999999),输出
N/A
。
输出:
1 A
1 C
1 E
1 A
3 A
N/A
解释:
310101
平均分最高(90.3),A 科第一;310102
C 科最高(95),排名第一;310103
E 分最高(94),E 科第一;310104
所有分数都是 91,总评也第一,A 科优先;310105
各科均非第一,但平均排名第三;999999
不存在,输出N/A
。
🔍 解题思路
本题考察多字段排序、排名处理与查询映射。
📎 变量说明表格
变量名 | 类型 | 含义 |
---|---|---|
n, m | int | 学生数量、查询次数 |
b[] | int[] | 学号 → 数组下标的映射表(-1 表示不存在) |
s[] | Stu[] | 学生信息结构体数组 |
rk | int | 当前学生的最优排名 |
cs | char | 当前学生的最优科目(A/C/M/E) |
✅ Step 1:定义结构体与成绩处理
每个学生有三门成绩以及平均成绩,结构体定义如下:
struct Stu {int id, c, m, e;double a;int rk;char cs;
};
读入数据时同时计算平均成绩 A,并将最优排名初始化为极大值:
cin >> s[i].id >> s[i].c >> s[i].m >> s[i].e;
s[i].a = (s[i].c + s[i].m + s[i].e) / 3.0;
s[i].rk = 1e9;
✅ Step 2:通用排名处理函数设计
为避免对 A/C/M/E 四科重复排序逻辑,我们设计一个通用函数:
void process_rank(Stu s[], int n, char course,bool (*cmp)(Stu, Stu), double (*getScore)(Stu)) {sort(s + 1, s + 1 + n, cmp);int rank = 1;for (int i = 1; i <= n; i++) {if (i > 1 && getScore(s[i]) != getScore(s[i - 1]))rank = i;if (rank < s[i].rk) {s[i].rk = rank;s[i].cs = course;}}
}
✏️ 参数说明:
cmp
:排序比较函数,例如cmpa()
按平均成绩排序;getScore
:获取对应科目成绩的函数,如getA()
返回s.a
;course
:当前处理科目名(A/C/M/E);
这样我们只需定义好各科比较与提取函数:
bool cmpa(Stu x, Stu y) { return x.a > y.a; }
double getA(Stu s) { return s.a; }
// 同理定义 cmpc/getC, cmpm/getM, cmpe/getE
调用统一处理四科排序并更新最优排名:
process_rank(s, n, 'A', cmpa, getA);
process_rank(s, n, 'C', cmpc, getC);
process_rank(s, n, 'M', cmpm, getM);
process_rank(s, n, 'E', cmpe, getE);
✅ Step 3:建立映射并处理查询
为实现 O(1) 查询学号信息,我们用数组记录每个学生编号出现的下标:
b[s[i].id] = i; // 记录学号对应下标
处理查询时直接查表输出:
if (b[x] == -1)cout << "N/A";
elsecout << s[b[x]].rk << " " << s[b[x]].cs;
✅ 完整代码(C++)
#include <bits/stdc++.h>
using namespace std;int n, m, b[1000005];struct Stu {int id, c, m, e;double a;int rk;char cs;
} s[2005];bool cmpa(Stu x, Stu y) { return x.a > y.a; }
bool cmpc(Stu x, Stu y) { return x.c > y.c; }
bool cmpm(Stu x, Stu y) { return x.m > y.m; }
bool cmpe(Stu x, Stu y) { return x.e > y.e; }double getA(Stu s) { return s.a; }
double getC(Stu s) { return s.c; }
double getM(Stu s) { return s.m; }
double getE(Stu s) { return s.e; }void process_rank(Stu s[], int n, char course,bool (*cmp)(Stu, Stu), double (*getScore)(Stu)) {sort(s + 1, s + 1 + n, cmp);int rank = 1;for (int i = 1; i <= n; i++) {if (i > 1 && getScore(s[i]) != getScore(s[i - 1]))rank = i;if (rank < s[i].rk) {s[i].rk = rank;s[i].cs = course;}}
}int main() {cin >> n >> m;memset(b, -1, sizeof(b));for (int i = 1; i <= n; i++) {cin >> s[i].id >> s[i].c >> s[i].m >> s[i].e;s[i].a = (s[i].c + s[i].m + s[i].e) / 3.0;s[i].rk = 1e9;}process_rank(s, n, 'A', cmpa, getA);process_rank(s, n, 'C', cmpc, getC);process_rank(s, n, 'M', cmpm, getM);process_rank(s, n, 'E', cmpe, getE);for (int i = 1; i <= n; i++)b[s[i].id] = i;while (m--) {int x;cin >> x;if (b[x] == -1) {cout << "N/A";} else {cout << s[b[x]].rk << " " << s[b[x]].cs;}if(m) cout << "\n";}return 0;
}
🚧 常见错误提醒
错误类型 | 具体表现 |
---|---|
并列排名未处理 | 相同分数未维持同一名次 |
忘记更新最优科目 | 排名更新时未赋值 cs |
未初始化 rk | 忘记设为极大值可能导致排名错乱 |
查询学号不存在未判断 | b[x] == -1 判断缺失,输出错误 |
✅ 总结归纳
- 核心:多字段排名 + 最优解取值;
- 统一封装函数提升复用性与可维护性;
- 查询用数组或哈希表快速定位。
📊 复杂度分析
- 时间复杂度
- 排序次数:4 次,每次 O(nlogn)\mathcal{O}(n \log n)O(nlogn);
- 查询次数:mmm 次,每次 O(1)\mathcal{O}(1)O(1);
- 总复杂度:
O(nlogn+m) \mathcal{O}(n \log n + m) O(nlogn+m)
-
空间复杂度
主要来源于学生数据与映射数组,约为:
O(n+106) \mathcal{O}(n + 10^6) O(n+106)
🧠 思维拓展
- 如果课程增多,如何更灵活地支持动态排序?
- 能否使用
map<int, Stu>
实现学号查找替代数组? - 支持查询所有科目的排名并排序输出,该如何扩展?