{ }codelper
null valuesP0286

Three hops to the power box contact

A campsite has n guests, m sites and k power boxes, each numbered from 1. There are three tables. The first table lists, for each guest, the number of the site assigned to that guest. The second table lists, for each site, the number of the power box it is connected to. The third table lists, for each power box, the name of the staff member in charge. In all three tables, an entry that has not been decided is written as - (a single minus sign) instead of a number or a name. q times you are given the number of a guest whose power went out. Each time, follow the tables in the order guest → site → power box → staff member, and if you reach the end, print the staff member's name. If you meet a - on the way, stop there and print - and the number of the step that broke, separated by one space. It is step 1 if the guest has no site, step 2 if that site has no power box, and step 3 if that power box has no staff member. Several guests may use the same site, and several sites may be connected to the same power box.

Input
The first line gives n, m, k and q separated by single spaces (1 ≤ n, m, k, q ≤ 100).
The second line gives n entries, for guest 1 onward: a site number (from 1 to m) or -.
The third line gives m entries, for site 1 onward: a power box number (from 1 to k) or -.
The fourth line gives k entries, for power box 1 onward: a staff name (1 to 8 lowercase English letters) or -.
Entries on a line are separated by single spaces.
Each of the next q lines gives one guest number (from 1 to n).
Output
q lines.
Each line is the staff member's name, or "- step" where step is 1, 2 or 3
Example 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
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

A value you looked up is used again as the index into the next table, so the languages differ in what happens when "nothing" is used as an index.
How to hold nothing: Python None, JS/TS null, Java null in Integer and String (an int cannot hold it), C# int?, C++ std::optional<int>, Craykon null.
A C int has no "nothing", so 0, which is never a valid number here, is chosen to mean it; names are pointers, so they can be NULL.
Using nothing as an index: Python box_of[None] is a TypeError, Java throws NullPointerException the moment a null Integer is used as an index or stored in an int, and Craykon stops the program at boxOf[null] (calculating with null stops too).
In TS strict mode, indexing with number | null is a compile error (TS2538), so the === null check cannot be forgotten, and in C# an int? cannot be used as an index as is, so you take it out with .Value.
JS, on the other hand, returns undefined for boxOf[null] without any error.
So in every language write "look up → check for nothing → next lookup" in that order.
There are tools that let nothing flow to the end.
Java Optional.ofNullable(siteOf[g]).map(s -> boxOf[s]).map(b -> staffOf[b]) ends as an empty Optional if any step gives null.
But it does not tell you which step broke, so this problem needs a check at each step.
The ?. of JS/TS/C# only guards the thing before the dot; it does not guard a missing index inside the brackets.
Seven traps: (1) In JS, if you do all three lookups first, a later value is undefined, not null, when an earlier step is missing.
=== null does not catch it (== null catches both).
(2) In Python, if nothing is stored as -1, box_of[-1] reads the last entry without an error.
(3) In Java, int site = siteOf[g] with a null does not give 0; it throws.
(4) C# GetValueOrDefault() turns null into 0, so the missing step is skipped and the break seems to happen one step later.
(5) In C, if entry 0 is also nothing (0), nothing spreads all the way to the end.
Asking from the end answers step 3 wherever it broke, so ask from the first step.
(6) In C++, .value() on an empty optional throws std::bad_optional_access, and * on it is undefined behavior.
(7) In Craykon, writing the lookup before the check stops the program on the line that uses null as an index.

Pythonmain.py · 25 lines
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 lines
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 lines
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 lines
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 lines
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 lines
#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 lines
#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 lines
// "-"는 빈값으로 바꾼다. 맨 앞에 빈값을 하나 두어 번호 1이 1번 칸이 되게 한다수표읽기(){    표 = [null];    foreach (낱말 in Console.ReadLine().Split(" "))    {        if (낱말 == "-")        {            표.Add(null);        }        else        {            표.Add(낱말.ToNumber());        }    }    return 표;} q = Console.ReadLine().Split(" ")[3].ToNumber();자리표 = 수표읽기(); // 손님 → 자리전기함표 = 수표읽기(); // 자리 → 전기함담당표 = [null]; // 전기함 → 담당자foreach (낱말 in Console.ReadLine().Split(" ")){    if (낱말 == "-")    {        담당표.Add(null);    }    else    {        담당표.Add(낱말);    }} for (i = 0; i < q; i++){    손님 = Console.ReadLine().ToNumber();    자리 = 자리표[손님];    if (자리 == null)    {        Console.WriteLine("- 1");        continue;    }    전기함 = 전기함표[자리]; // 자리가 빈값이면 여기서 프로그램이 멈추므로, 위에서 먼저 걸렀다    if (전기함 == null)    {        Console.WriteLine("- 2");        continue;    }    담당 = 담당표[전기함];    if (담당 == null)    {        Console.WriteLine("- 3");    }    else    {        Console.WriteLine(담당);    }}