题目描述
小明来到某学校当老师,需要将学生按考试总分或单科分数进行排名,你能帮帮他吗?
输入描述:
第 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 语言
#include<stdio.h>#include<stdlib.h>#include<string.h>typedefstruct{charname[16];intscores[12];inttotal;}Student;Student stu[105];charsubjects[12][16];intrankIdx;// -1 表示按总分排,否则是科目下标intcmp(constvoid*a,constvoid*b){Student*x=(Student*)a;Student*y=(Student*)b;intsx=(rankIdx==-1)?x->total:x->scores[rankIdx];intsy=(rankIdx==-1)?y->total:y->scores[rankIdx];if(sx!=sy)returnsy-sx;// 分数降序returnstrcmp(x->name,y->name);// 姓名升序}intmain(){intn,m;scanf("%d %d",&n,&m);for(inti=0;i<m;i++)scanf("%s",subjects[i]);for(inti=0;i<n;i++){scanf("%s",stu[i].name);stu[i].total=0;for(intj=0;j<m;j++){scanf("%d",&stu[i].scores[j]);stu[i].total+=stu[i].scores[j];}}charrankSub[16];scanf("%s",rankSub);rankIdx=-1;for(inti=0;i<m;i++){if(strcmp(rankSub,subjects[i])==0){rankIdx=i;break;}}qsort(stu,n,sizeof(Student),cmp);for(inti=0;i<n;i++){printf("%s%c",stu[i].name,i==n-1?'\n':' ');}return0;}C++
#include<bits/stdc++.h>usingnamespacestd;structStudent{string name;vector<int>scores;inttotal;};intmain(){intn,m;cin>>n>>m;vector<string>subjects(m);map<string,int>subIdx;for(inti=0;i<m;i++){cin>>subjects[i];subIdx[subjects[i]]=i;}vector<Student>stu(n);for(inti=0;i<n;i++){cin>>stu[i].name;stu[i].scores.resize(m);stu[i].total=0;for(intj=0;j<m;j++){cin>>stu[i].scores[j];stu[i].total+=stu[i].scores[j];}}string rankSub;cin>>rankSub;intrankIdx=subIdx.count(rankSub)?subIdx[rankSub]:-1;sort(stu.begin(),stu.end(),[&](constStudent&a,constStudent&b){intsa=(rankIdx==-1)?a.total:a.scores[rankIdx];intsb=(rankIdx==-1)?b.total:b.scores[rankIdx];if(sa!=sb)returnsa>sb;// 分数降序returna.name<b.name;// 姓名升序});for(inti=0;i<n;i++){cout<<stu[i].name<<(i==n-1?"\n":" ");}return0;}Java
importjava.util.*;publicclassMain{publicstaticvoidmain(String[]args){Scannersc=newScanner(System.in);intn=sc.nextInt();intm=sc.nextInt();String[]subjects=newString[m];Map<String,Integer>subIdx=newHashMap<>();for(inti=0;i<m;i++){subjects[i]=sc.next();subIdx.put(subjects[i],i);}String[]names=newString[n];int[][]scores=newint[n][m];int[]total=newint[n];for(inti=0;i<n;i++){names[i]=sc.next();for(intj=0;j<m;j++){scores[i][j]=sc.nextInt();total[i]+=scores[i][j];}}StringrankSub=sc.next();Integerhit=subIdx.get(rankSub);finalintidx=(hit==null)?-1:hit;Integer[]order=newInteger[n];for(inti=0;i<n;i++)order[i]=i;Arrays.sort(order,(a,b)->{intsa=(idx==-1)?total[a]:scores[a][idx];intsb=(idx==-1)?total[b]:scores[b][idx];if(sa!=sb)returnsb-sa;// 分数降序returnnames[a].compareTo(names[b]);// 姓名升序});StringBuildersb=newStringBuilder();for(inti=0;i<n;i++){if(i>0)sb.append(' ');sb.append(names[order[i]]);}System.out.println(sb);}}JavaScript
constreadline=require('readline');constrl=readline.createInterface({input:process.stdin});constlines=[];rl.on('line',(line)=>lines.push(line.trim()));rl.on('close',()=>{const[n,m]=lines[0].split(/\s+/).map(Number);constsubjects=lines[1].split(/\s+/);constsubIdx={};subjects.forEach((s,i)=>{subIdx[s]=i;});conststudents=[];for(leti=0;i<n;i++){constparts=lines[2+i].split(/\s+/);constname=parts[0];constscores=parts.slice(1,1+m).map(Number);consttotal=scores.reduce((a,b)=>a+b,0);students.push({name,scores,total});}constrankSub=lines[2+n];constrankIdx=(rankSubinsubIdx)?subIdx[rankSub]:-1;students.sort((a,b)=>{constsa=(rankIdx===-1)?a.total:a.scores[rankIdx];constsb=(rankIdx===-1)?b.total:b.scores[rankIdx];if(sa!==sb)returnsb-sa;// 分数降序returna.name<b.name?-1:(a.name>b.name?1:0);// 姓名升序});console.log(students.map(s=>s.name).join(' '));});Python
n,m=map(int,input().split())subjects=input().split()sub_idx={s:ifori,sinenumerate(subjects)}students=[]for_inrange(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)defkey_fn(stu):name,scores,total=stu score=totalifrank_idx==-1elsescores[rank_idx]return(-score,name)# 分数取负实现降序,姓名默认升序students.sort(key=key_fn)print(' '.join(s[0]forsinstudents))复杂度分析
- 时间复杂度:
O(n * log n),主要是排序。读入和算总分是O(n * m),m < 10 可视为常数 - 空间复杂度:
O(n * m),存储所有学生成绩
n 最大不到 100,随便排。
总结一下
智能成绩表 = 自定义排序 + 科目映射查表。
核心口诀:
- 排名科目查得到,按单科排;查不到,按总分排
- 分数降序,同分按姓名升序
- 建一张"科目名到下标"的映射,是切换两种模式的关键
这道题本身不难,真正容易翻车的是同分处理:忘了写姓名这一级比较器,遇到同分用例就过不去。把比较器写全,两条规则一个都不能少。
你还在哪些排序题里栽过跟头?欢迎在评论区聊聊。