题目描述
小明来到某学校当老师,需要将学生按考试总分或单科分数进行排名,你能帮帮他吗?
输入描述:
第 1 行输入两个整数,学生人数 n 和科目数量 m。0 < n < 100,0 < m < 10。
第 2 行输入 m 个科目名称,彼此之间用空格隔开。科目名称只由英文字母构成,单个长度不超过 10 个字符。科目的出现顺序和后续输入的学生成绩一一对应,不会出现重复的科目名称。
第 3 行开始的 n 行,每行包含一个学生的姓名和该生 m 个科目的成绩(空格隔开),学生不会重名。学生姓名只由英文字母构成,长度不超过 10 个字符。成绩是 0 到 100 的整数,依次对应第 2 行中输入的科目。
末尾一行,输入用作排名的科目名称。若科目不存在,则按总分进行排序。
输出描述:
输出一行,按成绩排序后的学生名字,空格隔开。成绩相同时,按姓名字典序升序排列。
原题文件在输出描述"成绩相"处截断,这里按常见题型补全为"成绩相同时,按姓名字典序升序排列"。排序方向按常见排名习惯取降序(分数高的在前)。
示例 1:
输入:
3 2
yuwen shuxue
fangfang 95 90
xiaohong 88 95
xiaoxin 90 90
shuxue
输出:
xiaohong fangfang xiaoxin
示例 2(科目不存在,按总分排):
输入:
3 2
yuwen shuxue
fangfang 95 90
xiaohong 88 95
xiaoxin 90 90
english
输出:
fangfang xiaohong xiaoxin
讲个故事:班主任老李的排名难题
老李刚接手三年级二班,校长丢来一张成绩表,让他排个名。
"按数学成绩排。"校长撂下一句话就走了。
老李一看,fangfang 和 xiaoxin 数学都考了 90,这谁排前面?挠了半天头,末尾按姓名拼音排,fangfang 在 xiaoxin 前面,搞定。
结果第二天校长又改口:"按英语排。"老李翻遍成绩表,压根没有英语这科。那咋办?按总分排呗。
说白了,这道题就是模拟老李的整个排榜过程:指定科目就按科目排,科目不存在就按总分排,同分看名字。
核心原理:一次自定义排序
整道题就一个核心动作:排序。难点全在比较器上。
比较器有两条规则,按优先级从高到低:
- 按排名分数降序。分数高的排前面。
- 分数相同时,按姓名字典序升序。名字小的排前面。
排名分数怎么定?看末尾一行输入的科目名:
- 如果这科在科目表里,排名分数就是该科成绩
- 如果这科不存在,排名分数就是总分(所有科目之和)
所以关键是建一张"科目名到科目下标"的映射表,查一下就知道按哪科排。查不到就退化为总分排序。
拿示例 1 走一遍。按 shuxue 排,三人数学成绩分别是 fangfang 90、xiaohong 95、xiaoxin 90。降序排,xiaohong 95 分最高,排第一。剩下两人都是 90,按名字升序,fangfang 在 xiaoxin 前面。最终输出 xiaohong fangfang xiaoxin。
换成示例 2,english 这科不存在,就按总分。三人总分 fangfang 185、xiaohong 183、xiaoxin 180,降序就是 fangfang xiaohong xiaoxin。
怎么实现?
- 读入 n、m
- 读入 m 个科目名,建一张"科目名到下标"的映射
- 读入 n 个学生,每人存姓名、各科成绩,顺手算出总分
- 读入排名科目名,查映射;查到用对应科目成绩,查不到用总分
- 自定义排序:分数降序,同分按姓名升序
- 输出排序后的姓名,空格隔开
代码实现
C 语言
c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct {
char name[16];
int scores[12];
int total;
} Student;
Student stu[105];
char subjects[12][16];
int rankIdx; // -1 表示按总分排,否则是科目下标
int cmp(const void *a, const void *b) {
Student *x = (Student *)a;
Student *y = (Student *)b;
int sx = (rankIdx == -1) ? x->total : x->scores[rankIdx];
int sy = (rankIdx == -1) ? y->total : y->scores[rankIdx];
if (sx != sy) return sy - sx; // 分数降序
return strcmp(x->name, y->name); // 姓名升序
}
int main() {
int n, m;
scanf("%d %d", &n, &m);
for (int i = 0; i < m; i++) scanf("%s", subjects[i]);
for (int i = 0; i < n; i++) {
scanf("%s", stu[i].name);
stu[i].total = 0;
for (int j = 0; j < m; j++) {
scanf("%d", &stu[i].scores[j]);
stu[i].total += stu[i].scores[j];
}
}
char rankSub[16];
scanf("%s", rankSub);
rankIdx = -1;
for (int i = 0; i < m; i++) {
if (strcmp(rankSub, subjects[i]) == 0) { rankIdx = i; break; }
}
qsort(stu, n, sizeof(Student), cmp);
for (int i = 0; i < n; i++) {
printf("%s%c", stu[i].name, i == n - 1 ? '\n' : ' ');
}
return 0;
}
C++
cpp
#include <bits/stdc++.h>
using namespace std;
struct Student {
string name;
vector<int> scores;
int total;
};
int main() {
int n, m;
cin >> n >> m;
vector<string> subjects(m);
map<string, int> subIdx;
for (int i = 0; i < m; i++) {
cin >> subjects[i];
subIdx[subjects[i]] = i;
}
vector<Student> stu(n);
for (int i = 0; i < n; i++) {
cin >> stu[i].name;
stu[i].scores.resize(m);
stu[i].total = 0;
for (int j = 0; j < m; j++) {
cin >> stu[i].scores[j];
stu[i].total += stu[i].scores[j];
}
}
string rankSub;
cin >> rankSub;
int rankIdx = subIdx.count(rankSub) ? subIdx[rankSub] : -1;
sort(stu.begin(), stu.end(), [&](const Student &a, const Student &b) {
int sa = (rankIdx == -1) ? a.total : a.scores[rankIdx];
int sb = (rankIdx == -1) ? b.total : b.scores[rankIdx];
if (sa != sb) return sa > sb; // 分数降序
return a.name < b.name; // 姓名升序
});
for (int i = 0; i < n; i++) {
cout << stu[i].name << (i == n - 1 ? "\n" : " ");
}
return 0;
}
Java
java
import java.util.*;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
int m = sc.nextInt();
String[] subjects = new String[m];
Map<String, Integer> subIdx = new HashMap<>();
for (int i = 0; i < m; i++) {
subjects[i] = sc.next();
subIdx.put(subjects[i], i);
}
String[] names = new String[n];
int[][] scores = new int[n][m];
int[] total = new int[n];
for (int i = 0; i < n; i++) {
names[i] = sc.next();
for (int j = 0; j < m; j++) {
scores[i][j] = sc.nextInt();
total[i] += scores[i][j];
}
}
String rankSub = sc.next();
Integer hit = subIdx.get(rankSub);
final int idx = (hit == null) ? -1 : hit;
Integer[] order = new Integer[n];
for (int i = 0; i < n; i++) order[i] = i;
Arrays.sort(order, (a, b) -> {
int sa = (idx == -1) ? total[a] : scores[a][idx];
int sb = (idx == -1) ? total[b] : scores[b][idx];
if (sa != sb) return sb - sa; // 分数降序
return names[a].compareTo(names[b]); // 姓名升序
});
StringBuilder sb = new StringBuilder();
for (int i = 0; i < n; i++) {
if (i > 0) sb.append(' ');
sb.append(names[order[i]]);
}
System.out.println(sb);
}
}
JavaScript
javascript
const readline = require('readline');
const rl = readline.createInterface({input: process.stdin});
const lines = [];
rl.on('line', (line) => lines.push(line.trim()));
rl.on('close', () => {
const [n, m] = lines[0].split(/\s+/).map(Number);
const subjects = lines[1].split(/\s+/);
const subIdx = {};
subjects.forEach((s, i) => { subIdx[s] = i; });
const students = [];
for (let i = 0; i < n; i++) {
const parts = lines[2 + i].split(/\s+/);
const name = parts[0];
const scores = parts.slice(1, 1 + m).map(Number);
const total = scores.reduce((a, b) => a + b, 0);
students.push({name, scores, total});
}
const rankSub = lines[2 + n];
const rankIdx = (rankSub in subIdx) ? subIdx[rankSub] : -1;
students.sort((a, b) => {
const sa = (rankIdx === -1) ? a.total : a.scores[rankIdx];
const sb = (rankIdx === -1) ? b.total : b.scores[rankIdx];
if (sa !== sb) return sb - sa; // 分数降序
return a.name < b.name ? -1 : (a.name > b.name ? 1 : 0); // 姓名升序
});
console.log(students.map(s => s.name).join(' '));
});
Python
python
n, m = map(int, input().split())
subjects = input().split()
sub_idx = {s: i for i, s in enumerate(subjects)}
students = []
for _ in range(n):
parts = input().split()
name = parts[0]
scores = list(map(int, parts[1:1 + m]))
students.append((name, scores, sum(scores)))
rank_sub = input().strip()
rank_idx = sub_idx.get(rank_sub, -1)
def key_fn(stu):
name, scores, total = stu
score = total if rank_idx == -1 else scores[rank_idx]
return (-score, name) # 分数取负实现降序,姓名默认升序
students.sort(key=key_fn)
print(' '.join(s[0] for s in students))
复杂度分析
- 时间复杂度:
O(n * log n),主要是排序。读入和算总分是O(n * m),m < 10 可视为常数 - 空间复杂度:
O(n * m),存储所有学生成绩
n 最大不到 100,随便排。
总结一下
智能成绩表 = 自定义排序 + 科目映射查表。
核心口诀:
- 排名科目查得到,按单科排;查不到,按总分排
- 分数降序,同分按姓名升序
- 建一张"科目名到下标"的映射,是切换两种模式的关键
这道题本身不难,真正容易翻车的是同分处理:忘了写姓名这一级比较器,遇到同分用例就过不去。把比较器写全,两条规则一个都不能少。
你还在哪些排序题里栽过跟头?欢迎在评论区聊聊。