{ }codelper
推导式分组P0206

制作答卷评分表

给出考试的标准答案字符串 K。
题目编号为 1 到 L(L 是 K 的长度),K 的第 i 个字符是第 i 题的答案。
给出 n 名学生的答卷。
答卷是由 A 到 E 和 .(跳过)组成的字符串,长度至少为 1、至多为 L。
答卷比 L 短时,缺少的后面几题算作跳过。
答对得 3 分,答错得 -1 分,跳过得 0 分。
不用循环,而用列表推导式、生成器表达式以及 zip 系列、enumerate、sorted、itertools.groupby 这类内置工具构造并输出以下内容。
1) 按输入顺序,为每名学生输出一行 "名字 分数 答对数 答错数 跳过数"。
2) 把分数相同的学生分为一组,从分数高的组开始,每组输出一行 "分数 名字1 名字2 ..."。
组内的名字按输入顺序写出。
3) 把答错学生最多的题目编号从小到大在一行中用空格分隔输出。
如果没有任何人答错任何题,输出 -。

输入
第一行为 K(大写英文字母 A 到 E,1 ≤ L ≤ 100)。
第二行为 n(1 ≤ n ≤ 1000)。
接下来 n 行为 "名字 答卷"。
名字为 1 到 10 个小写英文字母和数字,互不相同。
输出
1) n 行,2) 不同分数个数那么多行,3) 一行
样例 1 · stdin
ABCDE
4
mina ABCDD
joon AB
sora .BCEA
ted EBC
stdout
mina 11 4 1 0
joon 6 2 0 3
sora 4 2 2 1
ted 5 2 1 2
11 mina
6 joon
5 ted
4 sora
5
样例 2 · stdin
CAB
3
amy CAB
bo C.B
cy CAB
stdout
amy 9 3 0 0
bo 6 2 0 1
cy 9 3 0 0
9 amy cy
6 bo
-
main.py
输入(stdin)

运行时这些行会依次作为 input()/标准输入传入(运行中不会等待输入)。

$ 就绪

请在电脑上打开以编写和运行代码。在手机上可以查看题目和题解。

其他语言怎么写?查看各语言题解 · 1 种语言题解

本题只接受 Python,因为列表推导式和生成器表达式是 Python 的语法。
其他语言用循环,或 map、filter(JS)、流的 groupingBy(Java)、LINQ 的 Zip、GroupBy(C#)来做同样的事。
在 Craykon 中则用 循环 和 字典() 来数。
在把每题变成 O、X、.
之一的推导式 ["." if a == "." else "O" if a == k else "X" for a, k in ...] 中,放在前面的 x if 条件 else y 是选值的表达式,而跟在后面的 if 是筛选条件,位置不同。
五个陷阱:① zip(答卷, 答案) 在较短的一方处停止,所以短答卷缺少的题目整个消失(跳过数变少)。
要用 itertools.zip_longest(答卷, 答案, fillvalue=".") 配对到底(从 3.10 起,长度必须一致时 zip(..., strict=True) 会报错)。
② itertools.groupby 只把相邻的相同值分为一组。
不先按分数排序,同一个分数会被分成好几组。
sorted 是稳定排序,所以即使用 reverse=True 降序排列,同分学生仍保持输入顺序。
③ enumerate 从 0 开始数。
题号要用 enumerate(..., start=1) 从 1 开始数。
④ 把条件表达式只写成"对就 O,否则 X",跳过的 . 也会算作答错。
⑤ 如果没有人答错,最多的答错数是 0,直接按它来选就会选中所有题目。
为 0 时保留空列表,用 " ".join(...) or "-" 在空字符串时输出 -。
True 相加时算作 1,所以可以用 sum(m[i] == "X" for ...) 这样的生成器表达式直接计数。

Pythonmain.py · 30 行
from itertools import groupby, zip_longest key = input().strip()n = int(input())rows = [input().split() for _ in range(n)] # 문항마다 O(맞음)·X(틀림)·.(건너뜀). 짧은 답안은 zip_longest가 "."로 채워 끝까지 짝짓는다sheets = [    (name, ["." if a == "." else "O" if a == k else "X" for a, k in zip_longest(ans, key, fillvalue=".")])    for name, ans in rows] # 1) 학생마다: 이름 점수 맞은수 틀린수 건너뛴수stats = [(name, 3 * m.count("O") - m.count("X"), m.count("O"), m.count("X"), m.count(".")) for name, m in sheets]print("\n".join(" ".join(map(str, s)) for s in stats))  def score(s):    return s[1]  # 2) 같은 점수끼리: groupby는 붙어 있는 것만 묶으므로 먼저 점수로 정렬한다(정렬은 안정적이라 입력 순서가 남는다)ranked = sorted(stats, key=score, reverse=True)print("\n".join(f"{pts} " + " ".join(s[0] for s in grp) for pts, grp in groupby(ranked, key=score))) # 3) 가장 많이 틀린 문항(번호는 1부터)wrong = [sum(m[i] == "X" for _, m in sheets) for i in range(len(key))]top = max(wrong)worst = [q for q, w in enumerate(wrong, start=1) if w == top] if top > 0 else []print(" ".join(map(str, worst)) or "-")