판매 기록이 입력이 끝날 때까지 한 줄에 하나씩 주어진다.
기록 한 줄은 상품 이름과 금액이고, 둘 사이는 공백(빈칸이나 탭) 하나 이상으로 나뉜다.
줄 앞뒤에도 공백이 있을 수 있다.
공백만 있거나 빈 줄은 기록이 아니니 건너뛴다. 빈 줄 뒤에도 기록이 더 있을 수 있다.
상품마다 "이름 건수 합계"를 한 줄씩, 그 상품이 처음 나온 차례로 출력하라.
마지막 줄에는 "TOTAL 전체건수 전체합계"를 출력하라.
합계는 모두 소수 둘째 자리까지 정확히 두 자리로 쓴다(12.5는 12.50, 7은 7.00).
기록이 하나도 없으면 "TOTAL 0 0.00"만 출력한다.
입력
기록은 0 ~ 10,000줄, 한 줄은 줄 바꿈을 빼고 100글자 이하, 서로 다른 이름은 1,000개 이하. 이름은 영어 소문자 1 ~ 12글자. 금액은 0 이상 10,000 이하이고 "7", "7.5", "0.05"처럼 소수점 아래가 없거나 1 ~ 2자리다
출력
상품마다 한 줄, 그리고 TOTAL 한 줄
예시 1 · stdin
mina 12.5
jun 3
mina 0.05
jun 7.25
stdout
mina 2 12.55
jun 2 10.25
TOTAL 4 22.80
main.py
오류 목록00F8 ↓ · ⇧F8 ↑
입력(stdin)
실행하면 이 줄들이 차례로 input()·표준 입력으로 들어가요(실행 중에 묻지 않아요).
$ 준비됐어요
코드를 입력해 풀려면 PC에서 열어 주세요. 모바일에서는 문제와 풀이를 볼 수 있어요.
다른 언어로는?언어별 풀이 보기 · 8개 언어 풀이
입력 끝을 알리는 방법이 언어마다 달라요. Python for line in sys.stdin, Java readLine()이 null, C# Console.ReadLine()이 null, C fgets가 NULL, C++ getline이 거짓, JS·TS는 readFileSync(0)으로 한꺼번에 읽어 줄로 나눠요. 크레이콘도 콘솔.줄읽기()가 빈값이면 끝이에요. 빈 줄("")은 입력 끝이 아니니, 빈 줄에서 멈추면 그 뒤 기록을 놓쳐요. 줄을 나눌 때 JS line.split(" ")은 빈칸이 둘이면 빈 조각 ""를 만들고, Number("")는 오류 없이 0이라 조용히 틀려요. trim() 뒤에 split(/\s+/)를 써요. Python split(), C# Split((char[])null, RemoveEmptyEntries), C++ >>, C sscanf, 크레이콘 나누기()는 공백이 여러 개여도 한 번에 나눠요. 소수 둘째 자리는 Python f"{x:.2f}", JS·TS x.toFixed(2), C·C++ printf("%.2f")로 써요. 서식 없이 쓰면 12.5나 0.30000000000000004(0.1 + 0.2)가 나와요. Java String.format과 C# ToString("F2")는 컴퓨터의 지역 설정을 따라서 독일어 설정이면 12,50이 나와요. 그래서 Locale.ROOT와 CultureInfo.InvariantCulture를 넘겨요. C에서 합을 float로 모으면 유효 숫자가 7자리쯤이라 합이 1억 가까이 되면 틀려요. double을 쓰세요. 크레이콘은 글자.소수자리맞추기(합, 2)로 쓰고, 이름의 차례는 사전()의 열쇠목록이 넣은 차례를 지켜 줘요.
Pythonmain.py · 19 줄
import systotals = {} # 이름 → [건수, 합계]. dict는 처음 넣은 차례를 지킨다all_count, all_sum = 0, 0.0for line in sys.stdin: # 입력이 끝날 때까지 line = line.strip() if not line: # 빈 줄은 건너뛴다(입력 끝이 아님) continue name, amount = line.split() amount = float(amount) if name not in totals: totals[name] = [0, 0.0] totals[name][0] += 1 totals[name][1] += amount all_count += 1 all_sum += amountfor name, (count, total) in totals.items(): print(f"{name} {count} {total:.2f}")print(f"TOTAL {all_count} {all_sum:.2f}")
JavaScriptmain.js · 20 줄
const lines = require("fs").readFileSync(0, "utf8").split("\n");const totals = new Map(); // 이름 → { count, sum }, 처음 넣은 차례let allCount = 0;let allSum = 0;for (const raw of lines) { const line = raw.trim(); if (line === "") continue; // 빈 줄은 건너뛴다 const [name, amount] = line.split(/\s+/); // 빈칸이 여러 개여도 한 번에 const x = Number(amount); if (!totals.has(name)) totals.set(name, { count: 0, sum: 0 }); const t = totals.get(name); t.count++; t.sum += x; allCount++; allSum += x;}const out = [];for (const [name, t] of totals) out.push(`${name} ${t.count} ${t.sum.toFixed(2)}`);out.push(`TOTAL ${allCount} ${allSum.toFixed(2)}`);console.log(out.join("\n"));
TypeScriptmain.ts · 22 줄
import { readFileSync } from "node:fs";interface Tally { count: number; sum: number }const lines: string[] = readFileSync(0, "utf8").split("\n");const totals = new Map<string, Tally>(); // 처음 넣은 차례를 지킨다let allCount = 0;let allSum = 0;for (const raw of lines) { const line: string = raw.trim(); if (line === "") continue; // 빈 줄은 건너뛴다 const [name, amount] = line.split(/\s+/); const x: number = Number(amount); const t: Tally = totals.get(name) ?? { count: 0, sum: 0 }; t.count++; t.sum += x; totals.set(name, t); allCount++; allSum += x;}const out: string[] = [];for (const [name, t] of totals) out.push(`${name} ${t.count} ${t.sum.toFixed(2)}`);out.push(`TOTAL ${allCount} ${allSum.toFixed(2)}`);console.log(out.join("\n"));
JavaMain.java · 33 줄
import java.io.BufferedReader;import java.io.IOException;import java.io.InputStreamReader;import java.util.LinkedHashMap;import java.util.Locale;import java.util.Map;public class Main { public static void main(String[] args) throws IOException { BufferedReader in = new BufferedReader(new InputStreamReader(System.in)); Map<String, Integer> counts = new LinkedHashMap<>(); // 처음 넣은 차례를 지킨다 Map<String, Double> sums = new LinkedHashMap<>(); int allCount = 0; double allSum = 0; String line; while ((line = in.readLine()) != null) { // null이면 입력 끝 line = line.trim(); if (line.isEmpty()) continue; String[] p = line.split("\\s+"); double x = Double.parseDouble(p[1]); counts.merge(p[0], 1, Integer::sum); sums.merge(p[0], x, Double::sum); allCount++; allSum += x; } StringBuilder out = new StringBuilder(); // Locale.ROOT: 소수점이 쉼표인 지역 설정에서도 12.50으로 for (String name : counts.keySet()) out.append(String.format(Locale.ROOT, "%s %d %.2f%n", name, counts.get(name), sums.get(name))); out.append(String.format(Locale.ROOT, "TOTAL %d %.2f%n", allCount, allSum)); System.out.print(out); }}
C#Main.cs · 40 줄
using System;using System.Collections.Generic;using System.Globalization;using System.Text;class Program{ static void Main() { var inv = CultureInfo.InvariantCulture; // 소수점이 늘 '.' var names = new List<string>(); // Dictionary는 차례를 약속하지 않아서 따로 둔다 var counts = new Dictionary<string, int>(); var sums = new Dictionary<string, double>(); int allCount = 0; double allSum = 0; string line; while ((line = Console.ReadLine()) != null) { line = line.Trim(); if (line.Length == 0) continue; string[] p = line.Split((char[])null, StringSplitOptions.RemoveEmptyEntries); double x = double.Parse(p[1], inv); if (!counts.ContainsKey(p[0])) { names.Add(p[0]); counts[p[0]] = 0; sums[p[0]] = 0; } counts[p[0]]++; sums[p[0]] += x; allCount++; allSum += x; } var sb = new StringBuilder(); foreach (string name in names) sb.Append(name).Append(' ').Append(counts[name]).Append(' ').Append(sums[name].ToString("F2", inv)).Append('\n'); sb.Append("TOTAL ").Append(allCount).Append(' ').Append(allSum.ToString("F2", inv)).Append('\n'); Console.Write(sb); }}
C++main.cpp · 39 줄
#include <cstdio>#include <iostream>#include <sstream>#include <string>#include <unordered_map>#include <vector>int main() { std::vector<std::string> names; // 처음 나온 차례 std::unordered_map<std::string, int> at; // 이름 → names의 자리 std::vector<int> counts; std::vector<double> sums; int allCount = 0; double allSum = 0; std::string line; while (std::getline(std::cin, line)) { // 실패하면 입력 끝 std::istringstream ss(line); // >>는 앞뒤·사이 빈칸을 모두 건너뛴다 std::string name; double x; if (!(ss >> name >> x)) continue; // 빈 줄 auto it = at.find(name); int i; if (it == at.end()) { i = (int)names.size(); at[name] = i; names.push_back(name); counts.push_back(0); sums.push_back(0); } else { i = it->second; } counts[i]++; sums[i] += x; allCount++; allSum += x; } for (std::size_t i = 0; i < names.size(); i++) std::printf("%s %d %.2f\n", names[i].c_str(), counts[i], sums[i]); std::printf("TOTAL %d %.2f\n", allCount, allSum);}
Cmain.c · 29 줄
#include <stdio.h>#include <string.h>#define MAXN 1000static char names[MAXN][13];static int counts[MAXN];static double sums[MAXN]; /* float는 유효 숫자가 7자리뿐이라 합이 커지면 틀린다 */static int k = 0;int main(void) { char line[256], name[16]; double x; int all_count = 0; double all_sum = 0; while (fgets(line, sizeof line, stdin)) { /* NULL이면 입력 끝 */ if (sscanf(line, "%12s %lf", name, &x) != 2) continue; /* 빈 줄 */ int i = 0; while (i < k && strcmp(names[i], name) != 0) i++; if (i == k) strcpy(names[k++], name); counts[i]++; sums[i] += x; all_count++; all_sum += x; } for (int i = 0; i < k; i++) printf("%s %d %.2f\n", names[i], counts[i], sums[i]); printf("TOTAL %d %.2f\n", all_count, all_sum); return 0;}
Craykonmain.cray · 30 줄
counts = 사전();sums = 사전();allCount = 0;allSum = 0;line = 콘솔.줄읽기();동안 (line != 빈값){ line = line.공백제거(); // 빈 줄은 건너뛴다(입력 끝은 빈값) 만약 (line != "") { p = line.나누기(); x = p[1].수로변환(); 만약 (!counts.있나(p[0])) { counts[p[0]] = 0; sums[p[0]] = 0; } counts[p[0]] += 1; sums[p[0]] += x; allCount += 1; allSum += x; } line = 콘솔.줄읽기();}각각 (name 에서 counts.열쇠목록){ 콘솔.쓰기(name + " " + counts[name] + " " + 글자.소수자리맞추기(sums[name], 2));}콘솔.쓰기("TOTAL " + allCount + " " + 글자.소수자리맞추기(allSum, 2));