import sysfrom collections import dequelines = sys.stdin.read().split("\n")n = int(lines[0])names = []const = [] # const[i] = i번 칸 식에 적힌 수들의 합refs = [] # refs[i] = i번 칸 식에 적힌 이름들(같은 이름이 두 번 나오면 두 번)for line in lines[1 : 1 + n]: w = line.split() names.append(w[0]) c = 0 r = [] for term in w[2::2]: # w = [이름, "=", 항, "+", 항, "+", ...] if term[0].isdigit(): c += int(term) else: r.append(term) const.append(c) refs.append(r)index = {name: i for i, name in enumerate(names)}users = [[] for _ in range(n)] # users[j] = j번 칸을 식에 쓰는 칸들(쓴 횟수만큼)waiting = [0] * n # 아직 값을 모르는, 식에 쓴 이름의 수for i in range(n): for name in refs[i]: users[index[name]].append(i) waiting[i] += 1# 위상 정렬(Kahn 알고리즘): 기다릴 것이 없는 칸부터 값을 정하고, 그 칸을 쓰는 칸의 기다림을 하나씩 줄인다value = const[:]known = [False] * nqueue = deque(i for i in range(n) if waiting[i] == 0)while queue: j = queue.popleft() known[j] = True for i in users[j]: value[i] += value[j] waiting[i] -= 1 if waiting[i] == 0: queue.append(i)# 끝까지 기다림이 남은 칸은 고리에 들었거나 고리에 든 칸을 (거쳐서라도) 쓴다print("\n".join(f"{names[i]} {value[i] if known[i] else 'ERR'}" for i in range(n)))
JavaScriptmain.js · 43 行
const lines = require("fs").readFileSync(0, "utf8").split("\n");const n = Number(lines[0]);const names = [];const value = []; // 처음엔 식에 적힌 수들의 합, 이름 칸의 값을 알게 될 때마다 더한다const refs = []; // refs[i] = i번 칸 식에 적힌 이름들(같은 이름이 두 번 나오면 두 번)for (let i = 1; i <= n; i++) { const w = lines[i].trim().split(" "); // [이름, "=", 항, "+", 항, ...] names.push(w[0]); let c = 0; const r = []; for (let k = 2; k < w.length; k += 2) { if (/^[0-9]/.test(w[k])) c += Number(w[k]); else r.push(w[k]); } value.push(c); refs.push(r);}const index = new Map(names.map((name, i) => [name, i]));const users = Array.from({ length: n }, () => []); // users[j] = j번 칸을 식에 쓰는 칸들(쓴 횟수만큼)const waiting = new Array(n).fill(0); // 아직 값을 모르는, 식에 쓴 이름의 수refs.forEach((r, i) => { for (const name of r) { users[index.get(name)].push(i); waiting[i]++; }});// 위상 정렬(Kahn 알고리즘): 기다릴 것이 없는 칸부터 값을 정하고, 그 칸을 쓰는 칸의 기다림을 하나씩 줄인다const known = new Array(n).fill(false);const queue = [];for (let i = 0; i < n; i++) if (waiting[i] === 0) queue.push(i);for (let head = 0; head < queue.length; head++) { const j = queue[head]; known[j] = true; for (const i of users[j]) { value[i] += value[j]; // 10^15까지라 배정밀도 수로도 정확하다 if (--waiting[i] === 0) queue.push(i); }}// 끝까지 기다림이 남은 칸은 고리에 들었거나 고리에 든 칸을 (거쳐서라도) 쓴다console.log(names.map((name, i) => `${name} ${known[i] ? value[i] : "ERR"}`).join("\n"));
TypeScriptmain.ts · 45 行
import { readFileSync } from "node:fs";const lines: string[] = readFileSync(0, "utf8").split("\n");const n: number = Number(lines[0]);const names: string[] = [];const value: number[] = []; // 처음엔 식에 적힌 수들의 합, 이름 칸의 값을 알게 될 때마다 더한다const refs: string[][] = []; // refs[i] = i번 칸 식에 적힌 이름들(같은 이름이 두 번 나오면 두 번)for (let i = 1; i <= n; i++) { const w: string[] = lines[i].trim().split(" "); // [이름, "=", 항, "+", 항, ...] names.push(w[0]); let c = 0; const r: string[] = []; for (let k = 2; k < w.length; k += 2) { if (/^[0-9]/.test(w[k])) c += Number(w[k]); else r.push(w[k]); } value.push(c); refs.push(r);}const index = new Map<string, number>(names.map((name, i): [string, number] => [name, i]));const users: number[][] = Array.from({ length: n }, (): number[] => []); // users[j] = j번 칸을 식에 쓰는 칸들(쓴 횟수만큼)const waiting: number[] = new Array<number>(n).fill(0); // 아직 값을 모르는, 식에 쓴 이름의 수refs.forEach((r, i) => { for (const name of r) { users[index.get(name)!].push(i); // 식에 쓴 이름은 모두 어느 줄에선가 정해진다 waiting[i]++; }});// 위상 정렬(Kahn 알고리즘): 기다릴 것이 없는 칸부터 값을 정하고, 그 칸을 쓰는 칸의 기다림을 하나씩 줄인다const known: boolean[] = new Array<boolean>(n).fill(false);const queue: number[] = [];for (let i = 0; i < n; i++) if (waiting[i] === 0) queue.push(i);for (let head = 0; head < queue.length; head++) { const j = queue[head]; known[j] = true; for (const i of users[j]) { value[i] += value[j]; // 10^15까지라 number로도 정확하다 if (--waiting[i] === 0) queue.push(i); }}// 끝까지 기다림이 남은 칸은 고리에 들었거나 고리에 든 칸을 (거쳐서라도) 쓴다console.log(names.map((name, i) => `${name} ${known[i] ? value[i] : "ERR"}`).join("\n"));
JavaMain.java · 63 行
import java.io.BufferedReader;import java.io.IOException;import java.io.InputStreamReader;import java.util.ArrayDeque;import java.util.ArrayList;import java.util.HashMap;import java.util.List;import java.util.Map;public class Main { public static void main(String[] args) throws IOException { BufferedReader in = new BufferedReader(new InputStreamReader(System.in)); int n = Integer.parseInt(in.readLine().trim()); String[] names = new String[n]; long[] value = new long[n]; // 처음엔 식에 적힌 수들의 합. 10^15까지라 long List<List<String>> refs = new ArrayList<>(); // i번 칸 식에 적힌 이름들(같은 이름이 두 번 나오면 두 번) Map<String, Integer> index = new HashMap<>(); for (int i = 0; i < n; i++) { String[] w = in.readLine().trim().split(" "); // [이름, "=", 항, "+", 항, ...] names[i] = w[0]; index.put(w[0], i); List<String> r = new ArrayList<>(); for (int k = 2; k < w.length; k += 2) { if (Character.isDigit(w[k].charAt(0))) value[i] += Long.parseLong(w[k]); else r.add(w[k]); } refs.add(r); } List<List<Integer>> users = new ArrayList<>(); // users.get(j) = j번 칸을 식에 쓰는 칸들(쓴 횟수만큼) for (int i = 0; i < n; i++) users.add(new ArrayList<>()); int[] waiting = new int[n]; // 아직 값을 모르는, 식에 쓴 이름의 수 for (int i = 0; i < n; i++) { for (String name : refs.get(i)) { users.get(index.get(name)).add(i); waiting[i]++; } } // 위상 정렬(Kahn 알고리즘): 기다릴 것이 없는 칸부터 값을 정하고, 그 칸을 쓰는 칸의 기다림을 하나씩 줄인다 boolean[] known = new boolean[n]; ArrayDeque<Integer> queue = new ArrayDeque<>(); for (int i = 0; i < n; i++) if (waiting[i] == 0) queue.add(i); while (!queue.isEmpty()) { int j = queue.poll(); known[j] = true; for (int i : users.get(j)) { value[i] += value[j]; if (--waiting[i] == 0) queue.add(i); } } // 끝까지 기다림이 남은 칸은 고리에 들었거나 고리에 든 칸을 (거쳐서라도) 쓴다 StringBuilder out = new StringBuilder(); for (int i = 0; i < n; i++) { out.append(names[i]).append(' '); if (known[i]) out.append(value[i]); else out.append("ERR"); out.append('\n'); } System.out.print(out); }}
C#Main.cs · 65 行
using System;using System.Collections.Generic;using System.Text;class Program{ static void Main() { int n = int.Parse(Console.ReadLine().Trim()); var names = new string[n]; var value = new long[n]; // 처음엔 식에 적힌 수들의 합. 10^15까지라 long var refs = new List<string>[n]; // i번 칸 식에 적힌 이름들(같은 이름이 두 번 나오면 두 번) var index = new Dictionary<string, int>(); for (int i = 0; i < n; i++) { string[] w = Console.ReadLine().Trim().Split(' '); // [이름, "=", 항, "+", 항, ...] names[i] = w[0]; index[w[0]] = i; refs[i] = new List<string>(); for (int k = 2; k < w.Length; k += 2) { if (char.IsDigit(w[k][0])) value[i] += long.Parse(w[k]); else refs[i].Add(w[k]); } } var users = new List<int>[n]; // users[j] = j번 칸을 식에 쓰는 칸들(쓴 횟수만큼) for (int i = 0; i < n; i++) users[i] = new List<int>(); var waiting = new int[n]; // 아직 값을 모르는, 식에 쓴 이름의 수 for (int i = 0; i < n; i++) { foreach (string name in refs[i]) { users[index[name]].Add(i); waiting[i]++; } } // 위상 정렬(Kahn 알고리즘): 기다릴 것이 없는 칸부터 값을 정하고, 그 칸을 쓰는 칸의 기다림을 하나씩 줄인다 var known = new bool[n]; var queue = new Queue<int>(); for (int i = 0; i < n; i++) if (waiting[i] == 0) queue.Enqueue(i); while (queue.Count > 0) { int j = queue.Dequeue(); known[j] = true; foreach (int i in users[j]) { value[i] += value[j]; if (--waiting[i] == 0) queue.Enqueue(i); } } // 끝까지 기다림이 남은 칸은 고리에 들었거나 고리에 든 칸을 (거쳐서라도) 쓴다 var sb = new StringBuilder(); for (int i = 0; i < n; i++) { sb.Append(names[i]).Append(' '); if (known[i]) sb.Append(value[i]); else sb.Append("ERR"); sb.Append('\n'); } Console.Write(sb.ToString()); }}
C++main.cpp · 61 行
#include <cctype>#include <iostream>#include <queue>#include <sstream>#include <string>#include <unordered_map>#include <vector>int main() { std::ios::sync_with_stdio(false); std::cin.tie(nullptr); std::string line; std::getline(std::cin, line); int n = std::stoi(line); std::vector<std::string> names(n); std::vector<long long> value(n, 0); // 처음엔 식에 적힌 수들의 합. 10^15까지라 long long std::vector<std::vector<std::string>> refs(n); // i번 칸 식에 적힌 이름들(같은 이름이 두 번 나오면 두 번) std::unordered_map<std::string, int> index; for (int i = 0; i < n; i++) { std::getline(std::cin, line); std::istringstream words(line); std::string term, sign; words >> names[i] >> sign; // 이름, "=" index[names[i]] = i; while (words >> term) { if (std::isdigit(static_cast<unsigned char>(term[0]))) value[i] += std::stoll(term); else refs[i].push_back(term); words >> sign; // "+" (줄 끝이면 읽지 못하고 끝난다) } } std::vector<std::vector<int>> users(n); // users[j] = j번 칸을 식에 쓰는 칸들(쓴 횟수만큼) std::vector<int> waiting(n, 0); // 아직 값을 모르는, 식에 쓴 이름의 수 for (int i = 0; i < n; i++) { for (const auto& name : refs[i]) { users[index[name]].push_back(i); waiting[i]++; } } // 위상 정렬(Kahn 알고리즘): 기다릴 것이 없는 칸부터 값을 정하고, 그 칸을 쓰는 칸의 기다림을 하나씩 줄인다 std::vector<bool> known(n, false); std::queue<int> queue; for (int i = 0; i < n; i++) if (waiting[i] == 0) queue.push(i); while (!queue.empty()) { int j = queue.front(); queue.pop(); known[j] = true; for (int i : users[j]) { value[i] += value[j]; if (--waiting[i] == 0) queue.push(i); } } // 끝까지 기다림이 남은 칸은 고리에 들었거나 고리에 든 칸을 (거쳐서라도) 쓴다 std::string out; for (int i = 0; i < n; i++) out += names[i] + " " + (known[i] ? std::to_string(value[i]) : "ERR") + "\n"; std::cout << out; return 0;}
Cmain.c · 90 行
#include <ctype.h>#include <stdio.h>#include <stdlib.h>#include <string.h>#define MAXN 5000#define MAXREF 10000static char names[MAXN][9];static long long value[MAXN]; /* 처음엔 식에 적힌 수들의 합. 10^15까지라 long long */static char refName[MAXREF][9]; /* 식에 적힌 이름들(같은 이름이 두 번 나오면 두 번) */static int refOwner[MAXREF]; /* 그 이름을 쓴 칸 */static int nref;static int sorted[MAXN]; /* 칸 번호를 이름 차례로 늘어놓은 것: 이름 → 번호를 이진 탐색으로 찾는다 */static int byName(const void* a, const void* b) { return strcmp(names[*(const int*)a], names[*(const int*)b]);}static int findCell(const char* name, int n) { int lo = 0, hi = n - 1; while (lo <= hi) { int mid = (lo + hi) / 2; int c = strcmp(names[sorted[mid]], name); if (c == 0) return sorted[mid]; if (c < 0) lo = mid + 1; else hi = mid - 1; } return -1;}/* 같은 칸을 쓰는 칸들을 배열로 엮는다: head[j]에서 시작해 next[e]로 이어지고, user[e]가 쓰는 칸 */static int head[MAXN], next[MAXREF], user[MAXREF];static int waiting[MAXN]; /* 아직 값을 모르는, 식에 쓴 이름의 수 */static int known[MAXN];static int queue[MAXN];int main(void) { char line[256]; if (!fgets(line, sizeof line, stdin)) return 0; int n = atoi(line); for (int i = 0; i < n; i++) { fgets(line, sizeof line, stdin); char* word = strtok(line, " \r\n"); /* 이름 */ strcpy(names[i], word); strtok(NULL, " \r\n"); /* "=" */ while ((word = strtok(NULL, " \r\n")) != NULL) { if (strcmp(word, "+") == 0) continue; if (isdigit((unsigned char)word[0])) { value[i] += atoll(word); } else { strcpy(refName[nref], word); refOwner[nref++] = i; } } } for (int i = 0; i < n; i++) { sorted[i] = i; head[i] = -1; } qsort(sorted, n, sizeof sorted[0], byName); for (int e = 0; e < nref; e++) { int j = findCell(refName[e], n); user[e] = refOwner[e]; next[e] = head[j]; head[j] = e; waiting[refOwner[e]]++; } /* 위상 정렬(Kahn 알고리즘): 기다릴 것이 없는 칸부터 값을 정하고, 그 칸을 쓰는 칸의 기다림을 하나씩 줄인다 */ int qhead = 0, qtail = 0; for (int i = 0; i < n; i++) if (waiting[i] == 0) queue[qtail++] = i; while (qhead < qtail) { int j = queue[qhead++]; known[j] = 1; for (int e = head[j]; e != -1; e = next[e]) { int i = user[e]; value[i] += value[j]; if (--waiting[i] == 0) queue[qtail++] = i; } } /* 끝까지 기다림이 남은 칸은 고리에 들었거나 고리에 든 칸을 (거쳐서라도) 쓴다 */ for (int i = 0; i < n; i++) { if (known[i]) printf("%s %lld\n", names[i], value[i]); else printf("%s ERR\n", names[i]); } return 0;}
Craykonmain.cray · 80 行
n = 控制台.读行().去空白().转数字();names = [];value = []; // 처음엔 식에 적힌 수들의 합, 이름 칸의 값을 알게 될 때마다 더한다refs = []; // refs[i] = i번 칸 식에 적힌 이름들(같은 이름이 두 번 나오면 두 번)index = 字典(); // 이름 → 칸 번호循环 (i = 0; i < n; i++){ w = 控制台.读行().去空白().分割(" "); // [이름, "=", 항, "+", 항, ...] names.添加(w[0]); index[w[0]] = i; c = 0; r = []; 循环 (k = 2; k < w.数量; k += 2) { 첫글자 = 字符.编码(w[k][0]); 如果 (첫글자 >= 48 && 첫글자 <= 57) { c += w[k].转数字(); } 否则 { r.添加(w[k]); } } value.添加(c); refs.添加(r);}users = []; // users[j] = j번 칸을 식에 쓰는 칸들(쓴 횟수만큼)循环 (i = 0; i < n; i++){ users.添加([]);}waiting = 新列表(n, 0); // 아직 값을 모르는, 식에 쓴 이름의 수循环 (i = 0; i < n; i++){ 遍历 (name 在 refs[i]) { users[index[name]].添加(i); waiting[i] += 1; }}// 위상 정렬(Kahn 알고리즘): 기다릴 것이 없는 칸부터 값을 정하고, 그 칸을 쓰는 칸의 기다림을 하나씩 줄인다known = 新列表(n, 假);queue = [];循环 (i = 0; i < n; i++){ 如果 (waiting[i] == 0) { queue.添加(i); }}循环 (head = 0; head < queue.数量; head++){ j = queue[head]; known[j] = 真; 遍历 (i 在 users[j]) { value[i] += value[j]; waiting[i] -= 1; 如果 (waiting[i] == 0) { queue.添加(i); } }}// 끝까지 기다림이 남은 칸은 고리에 들었거나 고리에 든 칸을 (거쳐서라도) 쓴다循环 (i = 0; i < n; i++){ 如果 (known[i]) { 控制台.输出(names[i] + " " + value[i]); } 否则 { 控制台.输出(names[i] + " ERR"); }}