按 (成绩降序, 报名号升序) 排序,第 floor(m*150/100) 名的成绩即分数线,所有不低于该分数的选手构成排序前缀,一并输出。
OJ: roj
题目 ID: 1180
难度:普及-
标签:排序模拟python
创建: 2026-09-29 22:27
更新: 2026-10-07 12:15
前置题目
noi_openjudge ch0110-03成绩排序B 的排序步骤直接复用 A 教的「负号翻转成降序 + 元组承载次级键」这一两级排序键写法,只是把姓名换成报名号,再在其上叠加取第 ⌊m×150/100⌋ 名定线、按分数统计同分者并输出前缀的额外流程。noi_openjudge ch0110-01谁考了第k名B 先按成绩降序排好序,再取第 r 名选手的成绩当分数线(代码 line = p[r].score),正是 A 教的「排序后按名次取元素」这一步的直接复用;B 在其上叠加了双关键字排序与「同分选手一并入面、按分数统计前缀」的新流程。roj 1179奖学金A 教的「三项排序键一次排序后取前几名」被 B 迁移为按 (成绩降序, 报名号升序) 排序再输出排序前缀,B 只是在其上叠加分数线 ⌊1.5m⌋ 与同分扩张统计的额外流程。
题目描述
世博会志愿者选拔中,1000 90 / 3239 88 / 2390 95 / 7231 84 / 1005 95 / 1001 88:第 88 5 加四行名单 1005 95 / 2390 95 / 1000 90 / 1001 88 / 3239 88。
思路
把
参考代码
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:27
# update_at: 2026-09-29 22:30
import sys
RATIO = 150 # 面试线定在计划录取人数的 150% 名次上
FULL = 100 # 百分号的分母
def solve() -> None:
data = iter(map(int, sys.stdin.buffer.read().split()))
n, admitted_plan = next(data), next(data) # 题目中的 n、m
players: list[tuple[int, int]] = [(next(data), next(data)) for _ in range(n)]
# 成绩从高到低;成绩相同按报名号从小到大,元组比较正好就是这两级
players.sort(key=lambda p: (-p[1], p[0]))
# 面试线 = 第 floor(m*150/100) 名的成绩;同分者全部进入面试,实际人数可能超过该名次
cut_rank = admitted_plan * RATIO // FULL
# 个别数据里 floor(m*1.5) 会超出总人数,此时没人被淘汰,分数线记作 0(成绩至少为 1)
line = players[cut_rank - 1][1] if cut_rank <= n else 0
admitted = [p for p in players if p[1] >= line]
out = [f"{line} {len(admitted)}"]
out += [f"{k} {s}" for k, s in admitted]
print('\n'.join(out))
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:34
* update_at: 2026-10-05 04:34
*/
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long ll;
const int MAXN = 5005; // n <= 5000,多开几个下标防止越界
// 一名选手的排序所需信息
struct Player {
ll id; // 报名号
ll score; // 笔试成绩
};
ll n, m;
Player p[MAXN]; // p[i] 表示第 i 名选手
// 排序规则:成绩从高到低,成绩相同报名号小的在前
bool cmp_player(const Player &a, const Player &b) {
if (a.score != b.score) return a.score > b.score;
return a.id < b.id;
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
cin >> n >> m;
for (ll i = 1; i <= n; i++) {
cin >> p[i].id >> p[i].score;
}
sort(p + 1, p + n + 1, cmp_player);
// 分数线 = 排名 floor(m*150/100) 的选手成绩
// 个别真实数据里 r 可能超过 n,此时按分数线 0 处理,所有人入面
ll r = m * 150 / 100;
ll line = 0;
if (r <= n) line = p[r].score;
// 实际人数按分数统计:与分数线同分的选手即使排名在 r 之后也要入面
ll cnt = 0;
for (ll i = 1; i <= n; i++) {
if (p[i].score >= line) cnt++;
}
// 排好序后入面者恰好构成前缀,直接输出前 cnt 名
cout << line << " " << cnt << "\n";
for (ll i = 1; i <= cnt; i++) {
cout << p[i].id << " " << p[i].score << "\n";
}
return 0;
}