A - フィギュアスケートの採点 / Figure Skating Scoring 解説 by admin 印刷ページ
Claude 4.6 Opus (Thinking)概要
\(N\) 人の選手がそれぞれ \(M\) 人の審査員から受けた点数について、最高点と最低点を1つずつ除いた合計(最終スコア)を計算し、最終スコアが最も高い選手の番号を求める問題です。
考察
重要な気づき
各選手の最終スコアは「全審査員の点数の合計 − 最高点 − 最低点」で求められます。つまり、わざわざ配列をソートして最高点・最低点を除外する必要はなく、合計・最小値・最大値の3つを求めるだけで十分です。
具体例
例えば、ある選手が5人の審査員から [8, 6, 9, 7, 10] という点数を受けた場合:
- 合計: \(8 + 6 + 9 + 7 + 10 = 40\)
- 最低点: \(6\)、最高点: \(10\)
- 最終スコア: \(40 - 6 - 10 = 24\)
素朴なアプローチとの比較
素朴に各選手の点数をソートして最高点・最低点を除外する方法では、ソートに \(O(M \log M)\) かかります。一方、合計・最小値・最大値を1回の走査で求めれば \(O(M)\) で済みます。本問では \(N \times M \leq 10^6\) なのでソートでも間に合いますが、走査のみの方が定数倍が軽く確実です。
入力の高速化
\(N \times M\) が最大 \(10^6\) と大きいため、Pythonでは sys.stdin.buffer.read() で入力を一括読み込みすることで、1行ずつ input() を呼ぶよりも大幅に高速化できます。
アルゴリズム
- \(N\) と \(M\) を読み込む。
- 各選手 \(i\)(\(1 \leq i \leq N\))について:
- \(M\) 個の点数を読み込みながら、合計・最小値・最大値 を1回の走査で求める。
- 最終スコアを \(\text{合計} - \text{最小値} - \text{最大値}\) として計算する。
- 最終スコアが最大の選手の番号を記録する。同点の場合は番号が小さい方を優先する(先に見つかった方を保持すればよい)。
- 結果を出力する。
計算量
- 時間計算量: \(O(N \times M)\)
- 各選手について \(M\) 個の点数を1回走査するため。
- 空間計算量: \(O(N \times M)\)
- 入力を一括読み込みするための領域。各選手の処理自体は \(O(M)\) のスライスで済む。
実装のポイント
入力の一括読み込み:
sys.stdin.buffer.read().split()で全入力をバイト列として一括読み込みし、トークンに分割します。これによりPythonでも高速に入力を処理できます。最小値・最大値の初期化: 点数は \(1\) 以上 \(100\) 以下なので、最小値の初期値を \(101\)、最大値の初期値を \(0\) にすることで、最初の要素で必ず更新されるようにしています。
同点時の処理:
final_score > best_scoreと厳密な不等号で比較しているため、同スコアの場合は先に登場した(番号が小さい)選手が保持されます。特別な処理は不要です。int変換の遅延: スライスした各要素をint(s)で変換しながら合計・最小・最大を同時に更新することで、無駄なリスト生成を避けています。ソースコード
import sys
def main():
input_data = sys.stdin.buffer.read().split()
idx = 0
N = int(input_data[idx]); idx += 1
M = int(input_data[idx]); idx += 1
best_score = -1
best_player = 1
for i in range(1, N + 1):
scores = input_data[idx:idx + M]
idx += M
total = 0
mn = 101
mx = 0
for s in scores:
v = int(s)
total += v
if v < mn:
mn = v
if v > mx:
mx = v
final_score = total - mn - mx
if final_score > best_score:
best_score = final_score
best_player = i
print(best_player)
main()
この解説は claude4.6opus-thinking によって生成されました。
投稿日時:
最終更新: