奖学金

GitHub跳转原题关系图返回列表

把「总分降序、语文降序、学号升序」三段规则合成一个排序键 (-总分, -语文, 学号),一次排序取前 5 名。

OJ: roj

题目 ID: 1179

难度:入门

标签:排序模拟python

创建: 2026-09-29 22:29

更新: 2026-10-05 04:26

题目描述

nn 名学生(n<300n<300)按「总分降序,总分相同语文降序,再相同学号升序」排名,输出前 55 名的学号和总分。学号即输入行号(从 11 开始),每科成绩 0∼1000\sim100;输入第一行 nn,之后 nn 行每行三个整数。样例输入 6 + 90 67 80 / 87 66 91 / 78 89 91 / 88 99 77 / 67 89 64 / 78 89 98,输出 6 265 / 4 264 / 3 258 / 2 244 / 1 237。

思路

先算出每人总分 t=c+m+et=c+m+e,再依次比总分、语文、学号三项排序,取前 55 名输出。学号只是输入行号,不能拿输入顺序当名次。

参考代码

Python 版:

python
#!/usr/bin/env python3
# Author by Rainboy blog: https://rainboylv.com github: https://github.com/rainboylvx
# rbook: -> https://rbook.roj.ac.cn  https://rbook2.roj.ac.cn
# rainboy的学习导航网站: https://idx.roj.ac.cn
# create_at: 2026-09-29 22:29
# update_at: 2026-10-04 09:35

import sys

TOP = 5      # 只有前 5 名拿奖学金
SUBJECTS = 3  # 每人语文、数学、英语三门成绩


def rank_key(stu: tuple[int, int, int]) -> tuple[int, int, int]:
    """奖学金排序键:总分高者在前,同分看语文,再同学号小者在前。"""
    idx, total, chinese = stu
    return (-total, -chinese, idx)


def solve() -> None:
    data = iter(map(int, sys.stdin.buffer.read().split()))
    count = next(data)  # 参评学生人数

    # (学号, 总分, 语文),学号即输入行号,从 1 开始
    students = [
        (i + 1, sum(scores), scores[0])
        for i in range(count)
        if (scores := [next(data) for _ in range(SUBJECTS)])
    ]

    # 排序键统一取负号,把"从大到小"折成"从小到大",学号本身就是升序
    students.sort(key=rank_key)
    print('\n'.join(f"{idx} {total}" for idx, total, _ in students[:TOP]))


if __name__ == "__main__":
    solve()

C++ 版(同一算法):

cpp
/**
 * Author by Rainboy blog: https://rainboylv.com github: https://github.com/rainboylvx
 * rbook: -> https://rbook.roj.ac.cn  https://rbook2.roj.ac.cn
 * rainboy的学习导航网站: https://idx.roj.ac.cn
 * create_at: 2026-10-05 04:26
 * update_at: 2026-10-05 04:26
 */

#include <iostream>
#include <algorithm>
using namespace std;

typedef long long ll;

const int MAXN = 305; // n < 300,多开几个下标防止越界

// 一个学生的排序所需信息
struct Student {
    ll id;      // 学号,即输入行号(从 1 开始)
    ll total;   // 语文 + 数学 + 英语
    ll chinese; // 语文成绩,总分并列时用来比较
};

ll n;
Student stu[MAXN]; // stu[i] 表示学号为 i 的学生

// 题面规则:总分从高到低,总分相同看语文从高到低,再相同则学号小的在前。
bool cmp_student(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);

    cin >> n;
    for (ll i = 1; i <= n; i++) {
        ll chinese, math, english;
        cin >> chinese >> math >> english;
        stu[i].id = i;
        stu[i].total = chinese + math + english;
        stu[i].chinese = chinese;
    }

    sort(stu + 1, stu + n + 1, cmp_student);

    // 按名次输出前 5 名的学号与总分
    ll top = (n < 5 ? n : 5);
    for (ll i = 1; i <= top; i++) {
        cout << stu[i].id << " " << stu[i].total << "\n";
    }

    return 0;
}