A warehouse stock ledger has n lines recording goods moving in and out of cells.
Each line gives a zone name, a floor, a cell number and a change, separated by single spaces.
The zone name consists of letters, and uppercase and lowercase are different.
The floor is a nonzero integer, and basement floors are negative numbers written with a '-' in front.
The floor, the cell number and the change may have leading zeros, and the change may have a '+' in front.
So "A 07 3" and "A 7 03" are the same cell.
Add up the changes of the records whose zone name, floor and cell number are all the same.
A cell whose sum is 0 is not printed.
Print the remaining cells in ascending order of zone name; within the same zone, higher floors first; within the same floor, smaller cell numbers first.
Zone names are compared by the character code of the first position where they differ, and if one name is the beginning of the other, the shorter name comes first.
So every uppercase letter comes before every lowercase letter, "A" comes before "AB", and "AB" comes before "B".
On each line write the zone name, floor, cell number and sum separated by one space, and write the numbers without leading zeros or '+'.
On the last line print the number of cells printed and the sum of all changes, separated by one space; if no cell was printed, print only this line.
Input
The first line has n (1 ≤ n ≤ 5,000). Each of the next n lines has a zone name (1 to 3 letters), a floor (between −99 and 999, not 0), a cell number (between 1 and 9,999) and a change (between −100,000 and 100,000). Each number has 1 to 7 digits besides its sign
Output
One line "zone floor cell sum" for each cell with a nonzero sum, then a last line "cell-count total-sum"
Example 1 · stdin
9
B 2 5 +10
A 10 1 4
A 7 03 6
a 1 1 2
A 07 3 -1
A -1 12 3
B 2 5 -10
A 10 01 +1
AB 1 1 7
stdout
A 10 1 5
A 7 3 5
A -1 12 3
AB 1 1 7
a 1 1 2
5 22
main.py
Error List00F8 ↓ · ⇧F8 ↑
Input (stdin)
When you run, these lines are fed to input()/stdin in order (the program doesn't stop to ask).
$ Ready
Open this on a computer to write and run code. On mobile you can read the problem and the solutions.
In other languagesShow solutions by language · 8 languages
You build a dictionary whose key has three parts and order it in a different direction for each part. Python uses the tuple (zone, int(floor), int(cell)) directly as the key and flips only the sign of the floor, as in sort(key=lambda k: (k[0], -k[1], k[2])). C# also uses a tuple as a Dictionary key and chains OrderBy(...).ThenByDescending(...).ThenBy(...). Java uses a record Key as the key, and if you pass the comparison rule to a TreeMap, entries sit in that order from the moment they are inserted. C++ std::map<std::tuple<std::string, int, int>, int> is also kept in order, but you cannot choose the direction, so the floor is stored as -floor. C has no dictionary, so you sort the records with qsort to bring the same cell together and then merge neighbours. JS/TS and Craykon join the values, converted to numbers, into one string such as "A 7 3" for the key, and pass a comparison function when sorting (rows.sort((a, b) => ...), Craykon keys.Sort(order)). Trap one: if you build the key from the strings as written in the input, 07 and 7 become different keys. A 7 03 6 and A 07 3 -1 in the example are not merged and come out as two lines. Convert to numbers before building the key. Trap two: using an array [zone, floor, cell] as a JS Map key never merges anything, because arrays with the same contents are still different keys. The 9 records of the example come out as 9 lines. Trap three: floors and cells are compared as numbers. Compared as strings, "7" is greater than "10", so A 7 3 in the example comes before A 10 1. Sorting the string keys with keys.Sort() in Craykon also puts the floors in character order, so zone A of the example comes out as -1, 10, 7. Trap four: only the floor is descending. Python sort(reverse=True) reverses the whole sort, so the zones become descending too (a comes first in the example), and in Java, writing comparing(Key::zone).thenComparingInt(Key::floor).reversed() makes reversed() flip the whole chain before it, so the zones are reversed as well. Apply it to the floor only, as in thenComparing(Key::floor, Comparator.reverseOrder()). Iterating a C++ std::map with the floor stored as is gives lower floors first, so A -1 12 3 comes first in the example. Trap five: zone names are compared by character code. < in Python, JS/TS and Craykon, Java compareTo, C strcmp and < on C++ std::string follow character codes. But the default comparison of C# OrderBy(k => k.zone) and JS localeCompare follow dictionary order, so a in the example comes even before A. In C#, pass StringComparer.Ordinal. Trap six: if cells whose sum became 0 are not removed, B 2 5 0 appears in the example and the cell count becomes 6.
Pythonmain.py · 15 lines
import sysdata = sys.stdin.read().split("\n")n = int(data[0])total = {}for line in data[1:n + 1]: zone, floor, cell, delta = line.split() key = (zone, int(floor), int(cell)) # 07과 7이 같은 열쇠가 되도록 수로 바꿔 묶는다 total[key] = total.get(key, 0) + int(delta)keys = [k for k in total if total[k] != 0] # 합이 0인 칸은 빼고keys.sort(key=lambda k: (k[0], -k[1], k[2])) # 구역 오름차순, 층 내림차순, 칸 오름차순out = [f"{z} {f} {c} {total[(z, f, c)]}" for z, f, c in keys]out.append(f"{len(keys)} {sum(total.values())}")print("\n".join(out))
JavaScriptmain.js · 25 lines
const lines = require("fs").readFileSync(0, "utf8").split("\n");const n = Number(lines[0]);const total = new Map();for (let i = 1; i <= n; i++) { const [zone, floor, cell, delta] = lines[i].split(" "); // 배열은 열쇠로 쓸 수 없으니(같은 내용이어도 다른 열쇠) 수로 바꾼 뒤 문자열 하나로 만든다 const key = `${zone} ${Number(floor)} ${Number(cell)}`; total.set(key, (total.get(key) ?? 0) + Number(delta));}const rows = [];let grand = 0;for (const [key, sum] of total) { grand += sum; if (sum === 0) continue; // 합이 0인 칸은 뺀다 const [zone, floor, cell] = key.split(" "); rows.push({ zone, floor: Number(floor), cell: Number(cell), sum });}rows.sort((a, b) => { if (a.zone !== b.zone) return a.zone < b.zone ? -1 : 1; // 구역: 글자 코드 순 if (a.floor !== b.floor) return b.floor - a.floor; // 층: 높은 층 먼저 return a.cell - b.cell; // 칸: 작은 번호 먼저});const out = rows.map((r) => `${r.zone} ${r.floor} ${r.cell} ${r.sum}`);out.push(`${rows.length} ${grand}`);console.log(out.join("\n"));
TypeScriptmain.ts · 27 lines
import { readFileSync } from "node:fs";const lines: string[] = readFileSync(0, "utf8").split("\n");const n = Number(lines[0]);const total = new Map<string, number>();for (let i = 1; i <= n; i++) { const [zone, floor, cell, delta] = lines[i].split(" "); // 배열은 열쇠로 쓸 수 없으니(같은 내용이어도 다른 열쇠) 수로 바꾼 뒤 문자열 하나로 만든다 const key = `${zone} ${Number(floor)} ${Number(cell)}`; total.set(key, (total.get(key) ?? 0) + Number(delta));}const rows: { zone: string; floor: number; cell: number; sum: number }[] = [];let grand = 0;for (const [key, sum] of total) { grand += sum; if (sum === 0) continue; // 합이 0인 칸은 뺀다 const [zone, floor, cell] = key.split(" "); rows.push({ zone, floor: Number(floor), cell: Number(cell), sum });}rows.sort((a, b) => { if (a.zone !== b.zone) return a.zone < b.zone ? -1 : 1; // 구역: 글자 코드 순 if (a.floor !== b.floor) return b.floor - a.floor; // 층: 높은 층 먼저 return a.cell - b.cell; // 칸: 작은 번호 먼저});const out = rows.map((r) => `${r.zone} ${r.floor} ${r.cell} ${r.sum}`);out.push(`${rows.length} ${grand}`);console.log(out.join("\n"));
JavaMain.java · 41 lines
import java.io.BufferedReader;import java.io.InputStreamReader;import java.util.Comparator;import java.util.Map;import java.util.StringTokenizer;import java.util.TreeMap;public class Main { record Key(String zone, int floor, int cell) {} public static void main(String[] args) throws Exception { BufferedReader in = new BufferedReader(new InputStreamReader(System.in)); int n = Integer.parseInt(in.readLine().trim()); // 구역 오름차순, 층 내림차순, 칸 오름차순. 층에만 reverseOrder를 건다 Comparator<Key> order = Comparator.comparing(Key::zone) .thenComparing(Key::floor, Comparator.reverseOrder()) .thenComparingInt(Key::cell); TreeMap<Key, Integer> total = new TreeMap<>(order); for (int i = 0; i < n; i++) { StringTokenizer st = new StringTokenizer(in.readLine()); String zone = st.nextToken(); int floor = Integer.parseInt(st.nextToken()); int cell = Integer.parseInt(st.nextToken()); int delta = Integer.parseInt(st.nextToken()); total.merge(new Key(zone, floor, cell), delta, Integer::sum); } StringBuilder out = new StringBuilder(); int count = 0; int grand = 0; for (Map.Entry<Key, Integer> e : total.entrySet()) { int sum = e.getValue(); grand += sum; if (sum == 0) continue; // 합이 0인 칸은 뺀다 Key k = e.getKey(); out.append(k.zone()).append(' ').append(k.floor()).append(' ').append(k.cell()).append(' ').append(sum).append('\n'); count++; } out.append(count).append(' ').append(grand).append('\n'); System.out.print(out); }}
C#Main.cs · 32 lines
using System;using System.Collections.Generic;using System.Linq;using System.Text;class Program{ static void Main() { int n = int.Parse(Console.ReadLine()!.Trim()); var total = new Dictionary<(string zone, int floor, int cell), int>(); for (int i = 0; i < n; i++) { string[] p = Console.ReadLine()!.Split(' '); var key = (p[0], int.Parse(p[1]), int.Parse(p[2])); total[key] = total.GetValueOrDefault(key) + int.Parse(p[3]); } // 문자열은 꼭 StringComparer.Ordinal로: 기본 비교는 문화권 순서라 a가 B보다 앞에 온다 var keys = total.Keys.Where(k => total[k] != 0) .OrderBy(k => k.zone, StringComparer.Ordinal) .ThenByDescending(k => k.floor) .ThenBy(k => k.cell) .ToList(); var sb = new StringBuilder(); foreach (var k in keys) { sb.Append(k.zone).Append(' ').Append(k.floor).Append(' ').Append(k.cell).Append(' ').Append(total[k]).Append('\n'); } sb.Append(keys.Count).Append(' ').Append(total.Values.Sum()).Append('\n'); Console.Write(sb); }}
C++main.cpp · 28 lines
#include <iostream>#include <map>#include <string>#include <tuple>int main() { std::ios::sync_with_stdio(false); std::cin.tie(nullptr); int n; std::cin >> n; // std::map은 열쇠를 모두 오름차순으로 둔다. 층만 내림차순이 되도록 부호를 뒤집어 넣는다 std::map<std::tuple<std::string, int, int>, int> total; for (int i = 0; i < n; i++) { std::string zone; int floor, cell, delta; std::cin >> zone >> floor >> cell >> delta; total[{zone, -floor, cell}] += delta; } int count = 0, grand = 0; for (const auto& [key, sum] : total) { grand += sum; if (sum == 0) continue; // 합이 0인 칸은 뺀다 std::cout << std::get<0>(key) << ' ' << -std::get<1>(key) << ' ' << std::get<2>(key) << ' ' << sum << '\n'; count++; } std::cout << count << ' ' << grand << '\n'; return 0;}
Cmain.c · 43 lines
#include <stdio.h>#include <stdlib.h>#include <string.h>typedef struct { char zone[4]; int floor, cell, delta;} Rec;/* 구역 오름차순, 층 내림차순, 칸 오름차순 */static int cmp(const void *pa, const void *pb) { const Rec *a = pa, *b = pb; int z = strcmp(a->zone, b->zone); if (z != 0) return z; if (a->floor != b->floor) return a->floor > b->floor ? -1 : 1; if (a->cell != b->cell) return a->cell < b->cell ? -1 : 1; return 0;}int main(void) { static Rec r[5000]; int n; if (scanf("%d", &n) != 1) return 0; for (int i = 0; i < n; i++) { /* %d는 앞에 붙은 0과 '+'를 그대로 10진수로 읽는다 */ if (scanf("%3s %d %d %d", r[i].zone, &r[i].floor, &r[i].cell, &r[i].delta) != 4) return 0; } /* 사전이 없으니 정렬해서 같은 칸을 이웃하게 만든 뒤 묶는다 */ qsort(r, (size_t)n, sizeof r[0], cmp); int count = 0, grand = 0; for (int i = 0; i < n;) { int j = i, sum = 0; while (j < n && cmp(&r[i], &r[j]) == 0) sum += r[j++].delta; grand += sum; if (sum != 0) { /* 합이 0인 칸은 뺀다 */ printf("%s %d %d %d\n", r[i].zone, r[i].floor, r[i].cell, sum); count++; } i = j; } printf("%d %d\n", count, grand); return 0;}
Craykonmain.cray · 57 lines
n = Console.ReadLine().ToNumber();total = Dict();parts = Dict();grand = 0;for (i = 0; i < n; i++){ p = Console.ReadLine().Split(); floor = p[1].ToNumber(); cell = p[2].ToNumber(); delta = p[3].ToNumber(); // 07과 7이 같은 열쇠가 되도록 수로 바꾼 뒤 문자열 하나로 만든다 key = p[0] + " " + floor + " " + cell; if (total.Contains(key)) { total[key] += delta; } else { total[key] = delta; parts[key] = [p[0], floor, cell]; } grand += delta;}keys = [];foreach (key in total.Keys){ if (total[key] != 0) { keys.Add(key); }}keys.Sort(차례);foreach (key in keys){ Console.WriteLine(key + " " + total[key]);}Console.WriteLine(keys.Count + " " + grand);// 구역 오름차순, 층 내림차순, 칸 오름차순차례(앞, 뒤){ a = parts[앞]; b = parts[뒤]; if (a[0] != b[0]) { if (a[0] < b[0]) { return -1; } return 1; } if (a[1] != b[1]) { return b[1] - a[1]; } return a[2] - b[2];}