야영장에 손님 n명, 자리 m개, 전기함 k개가 있고, 저마다 1번부터 번호가 붙어 있다.
표가 세 개 있다.
첫째 표에는 손님마다 배정된 자리의 번호가 적혀 있다.
둘째 표에는 자리마다 연결된 전기함의 번호가 적혀 있다.
셋째 표에는 전기함마다 담당 직원의 이름이 적혀 있다.
세 표 모두, 정해지지 않은 칸에는 번호나 이름 대신 -(빼기 기호 하나)가 적혀 있다.
전기가 나갔다는 손님의 번호가 q번 주어진다.
그때마다 손님 → 자리 → 전기함 → 담당 직원의 차례로 표를 따라가서, 끝까지 닿으면 담당 직원의 이름을 출력하라.
가는 길에 - 를 만나면 거기서 멈추고, - 와 끊긴 단계의 번호를 빈칸 하나로 띄워 출력하라.
손님에게 자리가 없으면 1단계, 그 자리에 전기함이 없으면 2단계, 그 전기함에 담당 직원이 없으면 3단계다.
여러 손님이 같은 자리를 쓸 수 있고, 여러 자리가 같은 전기함에 연결될 수 있다.
입력
첫 줄에 n, m, k, q가 빈칸 하나로 띄워 주어진다(1 ≤ n, m, k, q ≤ 100). 둘째 줄에 1번 손님부터 차례로 자리 번호(1 이상 m 이하) 또는 - 가 n개, 셋째 줄에 1번 자리부터 차례로 전기함 번호(1 이상 k 이하) 또는 - 가 m개, 넷째 줄에 1번 전기함부터 차례로 담당 직원의 이름(영어 소문자 1~8글자) 또는 - 가 k개, 저마다 빈칸 하나로 띄워 주어진다. 다음 q줄에 손님 번호(1 이상 n 이하)가 하나씩 주어진다.
출력
q줄. 줄마다 담당 직원의 이름, 또는 "- 단계 번호"(단계 번호는 1, 2, 3 가운데 하나)
예시 1 · stdin
5 4 3 6
2 - 4 1 3
1 3 - 2
dana - miro
1
2
3
4
5
1
stdout
miro
- 1
- 3
dana
- 2
miro
main.py
오류 목록00F8 ↓ · ⇧F8 ↑
입력(stdin)
실행하면 이 줄들이 차례로 input()·표준 입력으로 들어가요(실행 중에 묻지 않아요).
$ 준비됐어요
코드를 입력해 풀려면 PC에서 열어 주세요. 모바일에서는 문제와 풀이를 볼 수 있어요.
다른 언어로는?언어별 풀이 보기 · 8개 언어 풀이
찾은 값을 다음 표의 번호로 다시 쓰는 문제라서, '없음'을 번호로 쓰면 어떻게 되는지가 언어마다 달라요. 없음을 담는 법: Python None, JS·TS null, Java Integer와 String의 null(int에는 못 담아요), C# int?, C++ std::optional<int>, 크레이콘 빈값. C의 int에는 없음이 없어서 번호로 쓰지 않는 0을 없음으로 정해 쓰고, 이름은 포인터라서 NULL을 쓸 수 있어요. 없음을 번호로 쓰면: Python box_of[None]은 TypeError, Java는 null인 Integer를 번호로 쓰거나 int에 담는 순간 NullPointerException, 크레이콘은 전기함표[빈값]에서 프로그램이 멈춰요(빈값으로 계산해도 멈춰요). TS는 엄격 모드(strict)에서 number | null을 번호로 쓰면 컴파일 오류(TS2538)라서 === null 검사를 빼먹을 수 없고, C#도 int?를 그대로 번호로 쓰면 컴파일 오류라 .Value로 꺼내야 해요. 반대로 JS boxOf[null]은 오류 없이 undefined를 줘요. 그래서 어느 언어든 '찾기 → 없음인지 확인 → 다음 찾기' 차례로 써요. 없음을 끝까지 흘려보내는 도구도 있어요. Java Optional.ofNullable(siteOf[g]).map(s -> boxOf[s]).map(b -> staffOf[b])는 중간에 null이 나오면 빈 Optional로 끝나요. 다만 어느 단계에서 끊겼는지는 알려 주지 않아서, 이 문제에서는 단계마다 물어야 해요. JS·TS·C#의 ?.는 점 앞의 것이 없을 때를 막아 줄 뿐, 대괄호 안의 번호가 없는 것은 막지 못해요. 함정 일곱: ① JS에서 세 값을 먼저 다 찾으면 앞 단계가 비었을 때 뒤의 값은 null이 아니라 undefined예요. === null로는 걸리지 않아요(== null은 둘 다 걸러요). ② Python에서 없음을 -1로 적어 두면 box_of[-1]이 오류 없이 맨 끝 칸을 읽어요. ③ Java에서 int site = siteOf[g]처럼 null을 int에 담으면 0이 되는 게 아니라 예외가 나요. ④ C# GetValueOrDefault()는 null을 0으로 바꿔서, 비어 있는 단계를 지나친 뒤 다음 단계에서 끊긴 것처럼 보이게 해요. ⑤ C에서 0번 칸도 없음(0)이면 없음이 끝까지 번져요. 끝에서부터 물으면 어디서 끊겼든 3단계라고 답하니, 앞 단계부터 물어요. ⑥ C++에서 빈 optional에 .value()를 부르면 std::bad_optional_access 예외가 나고, *를 쓰면 정의되지 않은 동작이에요. ⑦ 크레이콘에서 확인보다 찾기를 먼저 쓰면 빈값을 번호로 쓰는 줄에서 멈춰요.
Pythonmain.py · 25 줄
def read_row(convert): # "-"는 None으로, 나머지는 convert로 바꾼다. 맨 앞에 None을 하나 두어 번호 1이 1번 칸이 되게 한다 return [None] + [None if word == "-" else convert(word) for word in input().split()]n, m, k, q = map(int, input().split())site_of = read_row(int) # 손님 → 자리box_of = read_row(int) # 자리 → 전기함staff_of = read_row(str) # 전기함 → 담당자for _ in range(q): guest = int(input()) site = site_of[guest] if site is None: print("- 1") continue box = box_of[site] # site가 None이면 box_of[None]은 TypeError라서, 위에서 먼저 걸렀다 if box is None: print("- 2") continue staff = staff_of[box] if staff is None: print("- 3") else: print(staff)
JavaScriptmain.js · 28 줄
const lines = require("fs").readFileSync(0, "utf8").split("\n");const q = Number(lines[0].split(" ")[3]);// "-"는 null로 바꾼다. 맨 앞에 null을 하나 두어 번호 1이 1번 칸이 되게 한다function readRow(line, convert) { return [null, ...line.split(" ").map((word) => (word === "-" ? null : convert(word)))];}const siteOf = readRow(lines[1], Number); // 손님 → 자리const boxOf = readRow(lines[2], Number); // 자리 → 전기함const staffOf = readRow(lines[3], String); // 전기함 → 담당자const out = [];for (let i = 0; i < q; i++) { const guest = Number(lines[4 + i]); const site = siteOf[guest]; if (site === null) { out.push("- 1"); continue; } const box = boxOf[site]; // boxOf[null]은 오류 없이 undefined가 되므로, 위에서 먼저 걸렀다 if (box === null) { out.push("- 2"); continue; } const staff = staffOf[box]; out.push(staff === null ? "- 3" : staff);}console.log(out.join("\n"));
TypeScriptmain.ts · 30 줄
import { readFileSync } from "node:fs";const lines: string[] = readFileSync(0, "utf8").split("\n");const q = Number(lines[0].split(" ")[3]);// "-"는 null로 바꾼다. 맨 앞에 null을 하나 두어 번호 1이 1번 칸이 되게 한다function readRow<T>(line: string, convert: (word: string) => T): (T | null)[] { return [null, ...line.split(" ").map((word) => (word === "-" ? null : convert(word)))];}const siteOf: (number | null)[] = readRow(lines[1], Number); // 손님 → 자리const boxOf: (number | null)[] = readRow(lines[2], Number); // 자리 → 전기함const staffOf: (string | null)[] = readRow(lines[3], String); // 전기함 → 담당자const out: string[] = [];for (let i = 0; i < q; i++) { const guest = Number(lines[4 + i]); const site = siteOf[guest]; if (site === null) { out.push("- 1"); continue; } // 여기서부터 site는 number다. 위의 검사를 빼면 boxOf[site]는 컴파일 오류(null은 번호로 쓸 수 없다)가 된다 const box = boxOf[site]; if (box === null) { out.push("- 2"); continue; } const staff = staffOf[box]; out.push(staff === null ? "- 3" : staff);}console.log(out.join("\n"));
JavaMain.java · 45 줄
import java.io.BufferedReader;import java.io.IOException;import java.io.InputStreamReader;public class Main { // int에는 null을 담을 수 없어서 Integer를 쓴다. "-"는 null로 두고, 번호 1이 1번 칸이 되게 한 칸 밀어 담는다 static Integer[] readNumbers(String line) { String[] words = line.split(" "); Integer[] row = new Integer[words.length + 1]; for (int i = 0; i < words.length; i++) { row[i + 1] = words[i].equals("-") ? null : Integer.valueOf(words[i]); } return row; } public static void main(String[] args) throws IOException { BufferedReader in = new BufferedReader(new InputStreamReader(System.in)); int q = Integer.parseInt(in.readLine().split(" ")[3]); Integer[] siteOf = readNumbers(in.readLine()); // 손님 → 자리 Integer[] boxOf = readNumbers(in.readLine()); // 자리 → 전기함 String[] words = in.readLine().split(" "); String[] staffOf = new String[words.length + 1]; // 전기함 → 담당자 for (int i = 0; i < words.length; i++) { staffOf[i + 1] = words[i].equals("-") ? null : words[i]; } StringBuilder out = new StringBuilder(); for (int i = 0; i < q; i++) { int guest = Integer.parseInt(in.readLine().trim()); Integer site = siteOf[guest]; if (site == null) { out.append("- 1\n"); continue; } Integer box = boxOf[site]; // site가 null이면 번호로 쓰는 순간 NullPointerException이 난다 if (box == null) { out.append("- 2\n"); continue; } String staff = staffOf[box]; out.append(staff == null ? "- 3" : staff).append('\n'); } System.out.print(out); }}
C#Main.cs · 51 줄
using System;using System.Text;class Program{ // int? 는 '수 또는 null'이다. "-"는 null로 두고, 번호 1이 1번 칸이 되게 한 칸 밀어 담는다 static int?[] ReadNumbers(string line) { string[] words = line.Split(' '); var row = new int?[words.Length + 1]; for (int i = 0; i < words.Length; i++) { row[i + 1] = words[i] == "-" ? (int?)null : int.Parse(words[i]); } return row; } static void Main() { int q = int.Parse(Console.ReadLine().Split(' ')[3]); int?[] siteOf = ReadNumbers(Console.ReadLine()); // 손님 → 자리 int?[] boxOf = ReadNumbers(Console.ReadLine()); // 자리 → 전기함 string[] words = Console.ReadLine().Split(' '); var staffOf = new string[words.Length + 1]; // 전기함 → 담당자 for (int i = 0; i < words.Length; i++) { staffOf[i + 1] = words[i] == "-" ? null : words[i]; } var sb = new StringBuilder(); for (int i = 0; i < q; i++) { int guest = int.Parse(Console.ReadLine()); int? site = siteOf[guest]; if (site == null) { sb.Append("- 1\n"); continue; } int? box = boxOf[site.Value]; // int? 는 그대로 번호로 쓸 수 없다. 값이 있음을 확인한 뒤 .Value로 꺼낸다 if (box == null) { sb.Append("- 2\n"); continue; } string staff = staffOf[box.Value]; sb.Append(staff ?? "- 3").Append('\n'); } Console.Write(sb); }}
C++main.cpp · 45 줄
#include <iostream>#include <optional>#include <string>#include <vector>// std::optional<int>는 '수가 있거나 비어 있거나'다. "-"는 빈 optional로 두고, 번호 1이 1번 칸이 되게 한다std::vector<std::optional<int>> readNumbers(int count) { std::vector<std::optional<int>> row(count + 1); for (int i = 1; i <= count; i++) { std::string word; std::cin >> word; if (word != "-") row[i] = std::stoi(word); } return row;}int main() { int n, m, k, q; std::cin >> n >> m >> k >> q; auto siteOf = readNumbers(n); // 손님 → 자리 auto boxOf = readNumbers(m); // 자리 → 전기함 std::vector<std::optional<std::string>> staffOf(k + 1); // 전기함 → 담당자 for (int i = 1; i <= k; i++) { std::string word; std::cin >> word; if (word != "-") staffOf[i] = word; } for (int i = 0; i < q; i++) { int guest; std::cin >> guest; std::optional<int> site = siteOf[guest]; if (!site) { std::cout << "- 1\n"; continue; } std::optional<int> box = boxOf[*site]; // 비어 있는 optional에 *를 쓰면 정의되지 않은 동작이라, 먼저 확인했다 if (!box) { std::cout << "- 2\n"; continue; } const std::optional<std::string>& staff = staffOf[*box]; if (!staff) std::cout << "- 3\n"; else std::cout << *staff << '\n'; }}
Cmain.c · 44 줄
#include <stdio.h>#include <stdlib.h>#include <string.h>/* C의 int에는 '없음'이 없다. 번호가 1부터라서 0을 '없음'으로 정해 쓴다 */static void read_numbers(int *row, int count) { char word[16]; for (int i = 1; i <= count; i++) { scanf("%15s", word); row[i] = strcmp(word, "-") == 0 ? 0 : atoi(word); }}int main(void) { int n, m, k, q; scanf("%d %d %d %d", &n, &m, &k, &q); static int site_of[101], box_of[101]; static char names[101][16]; static const char *staff_of[101]; /* 포인터에는 '없음'이 있다: NULL */ read_numbers(site_of, n); read_numbers(box_of, m); for (int i = 1; i <= k; i++) { scanf("%15s", names[i]); staff_of[i] = strcmp(names[i], "-") == 0 ? NULL : names[i]; } for (int i = 0; i < q; i++) { int guest; scanf("%d", &guest); int site = site_of[guest]; if (site == 0) { puts("- 1"); continue; } int box = box_of[site]; if (box == 0) { puts("- 2"); continue; } const char *staff = staff_of[box]; if (staff == NULL) puts("- 3"); /* NULL을 puts에 넘기면 정의되지 않은 동작이다 */ else puts(staff); } return 0;}
Craykonmain.cray · 59 줄
// "-"는 빈값으로 바꾼다. 맨 앞에 빈값을 하나 두어 번호 1이 1번 칸이 되게 한다수표읽기(){ 표 = [빈값]; 각각 (낱말 에서 콘솔.줄읽기().나누기(" ")) { 만약 (낱말 == "-") { 표.넣기(빈값); } 아니면 { 표.넣기(낱말.수로변환()); } } 돌려주기 표;}q = 콘솔.줄읽기().나누기(" ")[3].수로변환();자리표 = 수표읽기(); // 손님 → 자리전기함표 = 수표읽기(); // 자리 → 전기함담당표 = [빈값]; // 전기함 → 담당자각각 (낱말 에서 콘솔.줄읽기().나누기(" ")){ 만약 (낱말 == "-") { 담당표.넣기(빈값); } 아니면 { 담당표.넣기(낱말); }}반복 (i = 0; i < q; i++){ 손님 = 콘솔.줄읽기().수로변환(); 자리 = 자리표[손님]; 만약 (자리 == 빈값) { 콘솔.쓰기("- 1"); 건너뛰기; } 전기함 = 전기함표[자리]; // 자리가 빈값이면 여기서 프로그램이 멈추므로, 위에서 먼저 걸렀다 만약 (전기함 == 빈값) { 콘솔.쓰기("- 2"); 건너뛰기; } 담당 = 담당표[전기함]; 만약 (담당 == 빈값) { 콘솔.쓰기("- 3"); } 아니면 { 콘솔.쓰기(담당); }}