{ }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()·표준 입력으로 들어가요(실행 중에 묻지 않아요).

$ 준비됐어요

코드를 입력해 풀려면 PC에서 열어 주세요. 모바일에서는 문제와 풀이를 볼 수 있어요.

다른 언어로는?언어별 풀이 보기 · 2개 언어 풀이

이 문제는 질의식 처리가 언어에 들어 있는 C#(LINQ)과 Java(스트림)만 받아요.
묶음 안에서 한 번 더 묶어야 해요.
C#은 GroupBy(판) 안에서 다시 GroupBy(선수)와 Max를 쓰고, Java는 groupingBy(판, TreeMap::new, toMap(선수, 점수, Math::max))처럼 모으기(collector)를 겹쳐 써요.
앞에서 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);    }}