{ }codelper
クエリ式(LINQ・ストリーム)レコードグループ化P0122

ステージごとの上位 k 人

ゲームの記録をレコード(record)にし、ループの代わりにクエリ式の処理(C# の LINQ、Java のストリーム)でまとめ、数え、並べ替えよ。
最初の行に記録の数 n と k が、続く n 行に記録が "ステージ 選手 点数" の形で一つずつ与えられる。
一人の選手が同じステージを何度も遊んだことがある。
ステージごとに、選手の点数はそのステージで得た最も高い点数一つとする。
ステージごとに、選手を点数の高い順に、点数が同じなら名前のコード順(ASCII、大文字が小文字より前)に並べる。
最初の行にステージの数を出力せよ。
続いて、ステージ名のコード順に、ステージごとに一行 "ステージ 選手数 名前:点数 ..." を出力せよ。
選手数はそのステージを遊んだ異なる選手の数で、その後に先頭から k 人を空白で区切って書く。
選手が k 人より少なければ全員を書く。
名前は大文字と小文字を区別するので、Ann と ann は別の選手である。

入力
1 ≤ n ≤ 5000、1 ≤ k ≤ 10。
ステージ名は英小文字 1〜8 文字で、異なるステージは 100 個以下である。
選手名は英大文字・小文字 1〜8 文字。
0 ≤ 点数 ≤ 1,000,000,000(整数)
出力
ステージの数、そしてステージごとに一行
例 1 · stdin
6 2
cave ann 50
sky Bob 7
cave bob 70
cave ann 90
sky adam 7
cave Ann 70
stdout
2
cave 3 ann:90 Ann:70
sky 2 Bob:7 adam:7
例 2 · stdin
5 3
moon zed 4
beach Zed 4
moon Zed 9
moon zed 9
moon eve 1
stdout
2
beach 1 Zed:4
moon 3 Zed:9 zed:9 eve:1
main.py
入力(stdin)

実行すると、これらの行が順に input()・標準入力に渡されます(実行中に入力を求めません)。

$ 準備完了

コードを入力して解くにはパソコンで開いてください。モバイルでは問題と解答を見られます。

ほかの言語では?言語別の解答を見る · 2 言語の解答

この問題はクエリ式の処理が言語に組み込まれた C#(LINQ)と Java(ストリーム)だけを受け付けます。
グループの中でもう一度まとめる必要があります。
C# は GroupBy(ステージ) の中でさらに GroupBy(選手) と Max を使い、Java は groupingBy(ステージ, TreeMap::new, toMap(選手, 点数, Math::max)) のようにコレクターを入れ子にします。
先頭の k 個は C# の Take(k)、Java の limit(k) で、k のほうが大きければあるだけを返します。
落とし穴は五つ:① 選手でまとめ直さずに記録をそのまま並べ替えると、同じ選手が順位に何度も入ります。
② Java の groupingBy の既定は HashMap なのでステージの順が決まっていません(TreeMap::new を渡します)。
C# の GroupBy は最初に出た順なので、別に OrderBy(g => g.Key) が必要です。
③ Java で comparingInt(Best::score).thenComparing(Best::player).reversed() のように reversed() を最後に付けると、それまでに作った比較全体が逆になり、名前の順も逆になります。
comparingInt(Best::score).reversed().thenComparing(Best::player) のように点数のすぐ後に付けます。
④ C# で OrderByDescending の後にまた OrderBy を書くと、前の並べ替えを捨てて最初から並べ替え直します。
二つ目の基準は ThenBy で書きます。
⑤ C# の OrderBy・ThenBy が文字列を比べる既定の方法はカルチャの規則なので、adam が Bob より前に来ます。
この問題はコード順(大文字が先)なので StringComparer.Ordinal を渡す必要があります。
Java の String.compareTo は最初からコード順です。

JavaMain.java · 44 行
import java.io.BufferedReader;import java.io.IOException;import java.io.InputStreamReader;import java.util.ArrayList;import java.util.Comparator;import java.util.List;import java.util.Map;import java.util.TreeMap;import java.util.stream.Collectors; record Play(String stage, String player, int score) {} record Best(String player, int score) {} public class Main {    public static void main(String[] args) throws IOException {        BufferedReader in = new BufferedReader(new InputStreamReader(System.in));        String[] first = in.readLine().trim().split(" ");        int n = Integer.parseInt(first[0]), k = Integer.parseInt(first[1]);        List<Play> plays = new ArrayList<>();        for (int i = 0; i < n; i++) {            String[] p = in.readLine().trim().split(" ");            plays.add(new Play(p[0], p[1], Integer.parseInt(p[2])));        }         // 판으로 묶고(TreeMap: 판 이름 순서) → 판 안에서 선수마다 가장 높은 점수(toMap + Math::max)        Map<String, Map<String, Integer>> best = plays.stream()            .collect(Collectors.groupingBy(Play::stage, TreeMap::new,                Collectors.toMap(Play::player, Play::score, Math::max)));         StringBuilder out = new StringBuilder().append(best.size()).append('\n');        best.forEach((stage, players) -> {            String top = players.entrySet().stream()                .map(e -> new Best(e.getKey(), e.getValue()))                .sorted(Comparator.comparingInt(Best::score).reversed() // 점수만 거꾸로                    .thenComparing(Best::player))                       // 이름은 그대로                .limit(k)                .map(b -> b.player() + ":" + b.score())                .collect(Collectors.joining(" "));            out.append(stage).append(' ').append(players.size()).append(' ').append(top).append('\n');        });        System.out.print(out);    }}
C#Main.cs · 42 行
using System;using System.Collections.Generic;using System.Linq;using System.Text; record Play(string Stage, string Player, int Score); class Program{    static void Main()    {        string[] first = Console.ReadLine()!.Trim().Split(' ');        int n = int.Parse(first[0]), k = int.Parse(first[1]);        var plays = new List<Play>();        for (int i = 0; i < n; i++)        {            string[] p = Console.ReadLine()!.Trim().Split(' ');            plays.Add(new Play(p[0], p[1], int.Parse(p[2])));        }         // 판으로 묶고 → 판 안에서 다시 선수로 묶어 가장 높은 점수 → 정렬해서 k명        var stages = plays            .GroupBy(r => r.Stage)            .OrderBy(g => g.Key, StringComparer.Ordinal) // 문화권 규칙이 아니라 코드 순서로            .Select(g =>            {                var ranked = g                    .GroupBy(r => r.Player)                    .Select(pg => (Player: pg.Key, Best: pg.Max(r => r.Score)))                    .OrderByDescending(x => x.Best)                    .ThenBy(x => x.Player, StringComparer.Ordinal)                    .ToList();                return (Stage: g.Key, Players: ranked.Count, Top: ranked.Take(k).Select(x => $"{x.Player}:{x.Best}"));            })            .ToList();         var sb = new StringBuilder().Append(stages.Count).Append('\n');        foreach (var s in stages)            sb.Append(s.Stage).Append(' ').Append(s.Players).Append(' ').Append(string.Join(" ", s.Top)).Append('\n');        Console.Write(sb);    }}