{ }codelper
深さ優先探索P0377

かくれんぼの鬼が歩いた道

家に部屋が n 個あり、1 番から n 番まで番号が付いている。
部屋ごとに、その部屋とドア 1 つで直接つながっている部屋の番号が与えられる。
ドアは両方向に通れるので、a 番の部屋のリストに b があれば、b 番の部屋のリストにも a がある。
鬼は 1 番の部屋から探し始め、友だちは t 番の部屋に隠れている。
鬼は次の規則を繰り返して動く。
今いる部屋と直接つながっている部屋の中に、まだ一度も入っていない部屋があれば、その中で番号がいちばん小さい部屋に入る。
そのような部屋がなければ、今いる部屋に初めて入ったときに出てきた部屋へ戻る。
t 番の部屋に入ると友だちを見つけたことになるので、すぐに止まる。
1 番の部屋にいて入れる部屋がなければ、友だちを見つけられないまま止まる。
1 番の部屋は最初から入ったことのある部屋として扱う。
1 行目に、鬼がドアを通った回数を出力せよ。
新しい部屋に入るときも戻るときも 1 回ずつ数える。
2 行目に、鬼がいた部屋の番号を最初の 1 番から順に、空白 1 つで区切って出力せよ。
戻ってもう一度立った部屋も、そのたびに書く。

入力
1 行目に n と t が空白 1 つで区切られて与えられる。
続く n 行のうち i 行目には、i 番の部屋とつながっている部屋の数 k と、その部屋の番号 k 個が空白 1 つずつで区切られて与えられる。
つながっている部屋がなければ、その行は 0 だけである。
2 ≤ n ≤ 30、2 ≤ t ≤ n、0 ≤ k ≤ n − 1。
1 行に書かれた部屋の番号はすべて異なり、自分の番号は含まれず、決まった順序なしに与えられる
出力
2 行。
1 行目にドアを通った回数、2 行目に鬼がいた部屋の番号を順に
例 1 · stdin
6 5
2 3 2
2 1 4
2 1 5
1 2
2 3 6
1 5
stdout
6
1 2 4 2 1 3 5
例 2 · stdin
10 9
2 10 2
1 1
1 9
0
0
0
0
0
1 3
1 1
stdout
4
1 2 1 10 1
main.py
入力(stdin)

実行すると、これらの行が順に input()・標準入力に渡されます(実行中に入力を求めません)。

$ 準備完了

コードを入力して解くにはパソコンで開いてください。モバイルでは問題と解答を見られます。

ほかの言語では?言語別の解答を見る · 8 言語の解答

再帰関数が「見つけた」を返すようにすると、深く入った場所からでも一気に抜け出せます。
if search(next): return True のように、返ってきた値が真ならすぐに真を返し、偽のときだけ「この部屋に戻った」と書きます。
この形は 8 つの解答すべてで同じで(C は bool の代わりに 1 と 0)、言語ごとに違うのは、つながっている部屋を番号順に並べ替える方法です。
Python sorted(nums)、Java Arrays.sort(int[])、C# Array.Sort(int[])、C++ std::sort、Craykon リスト.並べ替え() は数の大きさで並べ替えます。
C の qsort には必ず比較関数を渡します。
落とし穴 1:JS・TS の sort() は比較関数を渡さないと、数の配列でも文字列に変えて比べるので、10 が 2 より前に来ます。
例 2 では 1 番の部屋のリスト 10, 2 がそのまま残り、鬼が 10 番の部屋から入って、2 行目が 1 10 1 2 1 になります。
sort((a, b) => a - b) と書きます。
まったく並べ替えなくても例 2 は同じように間違い、例 1 は 3 番の部屋から入って 2 と 1 3 5 になります。
落とし穴 2:再帰呼び出しが返した値を見ないと、友だちを見つけた後も鬼が戻っていきます。
例 1 の 2 行目の終わりに 3 1 が余分に付き、1 行目が 8 になります。
落とし穴 3:初めて入った部屋だけを書くと(よくある「訪問順」の出力)、戻る歩みが抜けます。
例 1 が 4 と 1 2 4 3 5 になりますが、4 番の部屋から 3 番の部屋へ直接行くドアはありません。
部屋は 30 個しかないので、どの言語でも再帰の深さを心配する必要はありません。

Pythonmain.py · 30 行
import sys lines = sys.stdin.read().split("\n")n, target = map(int, lines[0].split())doors = [[] for _ in range(n + 1)]       # doors[r] = r번 방과 문으로 이어진 방들for r in range(1, n + 1):    nums = list(map(int, lines[r].split()))    doors[r] = sorted(nums[1:])          # 번호가 작은 방부터 들어가 보려고 정렬해 둔다 visited = [False] * (n + 1)walk = []                                # 술래가 있던 방을 차례대로 적는다  def search(room):    """room에 들어가서 찾는다. 친구를 찾았으면 True를 돌려준다."""    visited[room] = True    walk.append(room)    if room == target:        return True    for nxt in doors[room]:        if not visited[nxt]:            if search(nxt):                return True              # 찾았으면 되돌아가지 않고 바로 끝낸다            walk.append(room)            # 못 찾고 이 방으로 되돌아왔다    return False  search(1)print(len(walk) - 1)print(" ".join(map(str, walk)))
JavaScriptmain.js · 29 行
const lines = require("fs").readFileSync(0, "utf8").split("\n");const [n, target] = lines[0].trim().split(" ").map(Number);const doors = [[]]; // doors[r] = r번 방과 문으로 이어진 방들(0번은 쓰지 않는다)for (let r = 1; r <= n; r++) {  const nums = lines[r].trim().split(" ").map(Number);  // 번호가 작은 방부터 들어가 보려고 정렬해 둔다. sort()만 쓰면 문자열로 견주므로 함수를 넘긴다  doors.push(nums.slice(1).sort((a, b) => a - b));} const visited = new Array(n + 1).fill(false);const walk = []; // 술래가 있던 방을 차례대로 적는다 // room에 들어가서 찾는다. 친구를 찾았으면 true를 돌려준다function search(room) {  visited[room] = true;  walk.push(room);  if (room === target) return true;  for (const next of doors[room]) {    if (!visited[next]) {      if (search(next)) return true; // 찾았으면 되돌아가지 않고 바로 끝낸다      walk.push(room); // 못 찾고 이 방으로 되돌아왔다    }  }  return false;} search(1);console.log(walk.length - 1);console.log(walk.join(" "));
TypeScriptmain.ts · 31 行
import { readFileSync } from "node:fs"; const lines: string[] = readFileSync(0, "utf8").split("\n");const [n, target]: number[] = lines[0].trim().split(" ").map(Number);const doors: number[][] = [[]]; // doors[r] = r번 방과 문으로 이어진 방들(0번은 쓰지 않는다)for (let r = 1; r <= n; r++) {  const nums: number[] = lines[r].trim().split(" ").map(Number);  // 번호가 작은 방부터 들어가 보려고 정렬해 둔다. sort()만 쓰면 문자열로 견주므로 함수를 넘긴다  doors.push(nums.slice(1).sort((a: number, b: number): number => a - b));} const visited: boolean[] = new Array<boolean>(n + 1).fill(false);const walk: number[] = []; // 술래가 있던 방을 차례대로 적는다 // room에 들어가서 찾는다. 친구를 찾았으면 true를 돌려준다function search(room: number): boolean {  visited[room] = true;  walk.push(room);  if (room === target) return true;  for (const next of doors[room]) {    if (!visited[next]) {      if (search(next)) return true; // 찾았으면 되돌아가지 않고 바로 끝낸다      walk.push(room); // 못 찾고 이 방으로 되돌아왔다    }  }  return false;} search(1);console.log(walk.length - 1);console.log(walk.join(" "));
JavaMain.java · 53 行
import java.io.BufferedReader;import java.io.IOException;import java.io.InputStreamReader;import java.util.ArrayList;import java.util.Arrays;import java.util.List; public class Main {    static int target;    static int[][] doors;        // doors[r] = r번 방과 문으로 이어진 방들    static boolean[] visited;    static List<Integer> walk = new ArrayList<>(); // 술래가 있던 방을 차례대로 적는다     // room에 들어가서 찾는다. 친구를 찾았으면 true를 돌려준다    static boolean search(int room) {        visited[room] = true;        walk.add(room);        if (room == target) return true;        for (int next : doors[room]) {            if (!visited[next]) {                if (search(next)) return true; // 찾았으면 되돌아가지 않고 바로 끝낸다                walk.add(room);                // 못 찾고 이 방으로 되돌아왔다            }        }        return false;    }     public static void main(String[] args) throws IOException {        BufferedReader in = new BufferedReader(new InputStreamReader(System.in));        String[] first = in.readLine().trim().split(" ");        int n = Integer.parseInt(first[0]);        target = Integer.parseInt(first[1]);        doors = new int[n + 1][];        visited = new boolean[n + 1];        for (int r = 1; r <= n; r++) {            String[] parts = in.readLine().trim().split(" ");            int k = Integer.parseInt(parts[0]);            doors[r] = new int[k];            for (int i = 0; i < k; i++) {                doors[r][i] = Integer.parseInt(parts[i + 1]);            }            Arrays.sort(doors[r]); // int[]는 수의 크기로 정렬된다        }        search(1);        StringBuilder out = new StringBuilder();        out.append(walk.size() - 1).append('\n');        for (int i = 0; i < walk.size(); i++) {            if (i > 0) out.append(' ');            out.append(walk.get(i));        }        System.out.println(out);    }}
C#Main.cs · 50 行
using System;using System.Collections.Generic; class Program{    static int target;    static int[][] doors;       // doors[r] = r번 방과 문으로 이어진 방들    static bool[] visited;    static List<int> walk = new List<int>(); // 술래가 있던 방을 차례대로 적는다     // room에 들어가서 찾는다. 친구를 찾았으면 true를 돌려준다    static bool Search(int room)    {        visited[room] = true;        walk.Add(room);        if (room == target) return true;        foreach (int next in doors[room])        {            if (!visited[next])            {                if (Search(next)) return true; // 찾았으면 되돌아가지 않고 바로 끝낸다                walk.Add(room);                // 못 찾고 이 방으로 되돌아왔다            }        }        return false;    }     static void Main()    {        string[] first = Console.ReadLine().Trim().Split(' ');        int n = int.Parse(first[0]);        target = int.Parse(first[1]);        doors = new int[n + 1][];        visited = new bool[n + 1];        for (int r = 1; r <= n; r++)        {            string[] parts = Console.ReadLine().Trim().Split(' ');            int k = int.Parse(parts[0]);            doors[r] = new int[k];            for (int i = 0; i < k; i++)            {                doors[r][i] = int.Parse(parts[i + 1]);            }            Array.Sort(doors[r]); // int[]는 수의 크기로 정렬된다        }        Search(1);        Console.WriteLine(walk.Count - 1);        Console.WriteLine(string.Join(" ", walk));    }}
C++main.cpp · 45 行
#include <algorithm>#include <iostream>#include <vector>using namespace std; int target;vector<vector<int>> doors; // doors[r] = r번 방과 문으로 이어진 방들vector<bool> visited;vector<int> walk;          // 술래가 있던 방을 차례대로 적는다 // room에 들어가서 찾는다. 친구를 찾았으면 true를 돌려준다bool search(int room) {    visited[room] = true;    walk.push_back(room);    if (room == target) return true;    for (int next : doors[room]) {        if (!visited[next]) {            if (search(next)) return true; // 찾았으면 되돌아가지 않고 바로 끝낸다            walk.push_back(room);          // 못 찾고 이 방으로 되돌아왔다        }    }    return false;} int main() {    int n;    cin >> n >> target;    doors.resize(n + 1);    visited.assign(n + 1, false);    for (int r = 1; r <= n; r++) {        int k;        cin >> k;        doors[r].resize(k);        for (int &room : doors[r]) cin >> room;        sort(doors[r].begin(), doors[r].end()); // 번호가 작은 방부터    }    search(1);    cout << walk.size() - 1 << '\n';    for (size_t i = 0; i < walk.size(); i++) {        if (i > 0) cout << ' ';        cout << walk[i];    }    cout << '\n';    return 0;}
Cmain.c · 48 行
#include <stdio.h>#include <stdlib.h> #define MAXN 30 static int n, target;static int doors[MAXN + 1][MAXN]; /* doors[r] = r번 방과 문으로 이어진 방들 */static int door_count[MAXN + 1];static int visited[MAXN + 1];static int walk[2 * MAXN];        /* 술래가 있던 방을 차례대로 적는다(많아야 2n - 1개) */static int walk_len = 0; static int compare_ints(const void *a, const void *b) {    return *(const int *)a - *(const int *)b; /* 방 번호는 1~30이라 빼도 넘치지 않는다 */} /* room에 들어가서 찾는다. 친구를 찾았으면 1을 돌려준다 */static int search(int room) {    visited[room] = 1;    walk[walk_len++] = room;    if (room == target) return 1;    for (int i = 0; i < door_count[room]; i++) {        int next = doors[room][i];        if (!visited[next]) {            if (search(next)) return 1; /* 찾았으면 되돌아가지 않고 바로 끝낸다 */            walk[walk_len++] = room;    /* 못 찾고 이 방으로 되돌아왔다 */        }    }    return 0;} int main(void) {    scanf("%d %d", &n, &target);    for (int r = 1; r <= n; r++) {        scanf("%d", &door_count[r]);        for (int i = 0; i < door_count[r]; i++) {            scanf("%d", &doors[r][i]);        }        qsort(doors[r], door_count[r], sizeof(int), compare_ints);    }    search(1);    printf("%d\n", walk_len - 1);    for (int i = 0; i < walk_len; i++) {        printf(i > 0 ? " %d" : "%d", walk[i]);    }    printf("\n");    return 0;}
Craykonmain.cray · 40 行
첫줄 = コンソール.数読み();n = 첫줄[0];target = 첫줄[1];doors = [[]]; // doors[r] = r번 방과 문으로 이어진 방들(0번은 쓰지 않는다)繰り返す (r = 1; r <= n; r++){    nums = コンソール.数読み();    rooms = nums.部分リスト(1, nums[0]);    rooms.並べ替え(); // 수 목록은 수의 크기로 정렬된다. 번호가 작은 방부터 들어가 본다    doors.追加(rooms);}visited = 新リスト(n + 1, 偽);walk = []; // 술래가 있던 방을 차례대로 적는다 // room에 들어가서 찾는다. 친구를 찾았으면 참을 돌려준다찾기(room){    visited[room] = 真;    walk.追加(room);    もし (room == target)    {        返す 真;    }    それぞれ (next の中 doors[room])    {        もし (!visited[next])        {            もし (찾기(next))            {                返す 真; // 찾았으면 되돌아가지 않고 바로 끝낸다            }            walk.追加(room); // 못 찾고 이 방으로 되돌아왔다        }    }    返す 偽;} 찾기(1);コンソール.書く(walk.個数 - 1);コンソール.書く(walk.つなぐ(" "));