{ }codelper
深度优先搜索P0377

捉迷藏里找人者走过的路

一栋房子有 n 个房间,编号从 1 到 n。
对每个房间,给出与它由一扇门直接相连的房间的编号。
门可以双向通行,所以如果 a 号房间的列表里有 b,b 号房间的列表里也有 a。
找人者从 1 号房间开始找,朋友藏在 t 号房间。
找人者反复按下面的规则移动。
如果与当前房间直接相连的房间中有还一次都没进去过的,就进入其中编号最小的那个。
如果没有这样的房间,就回到第一次进入当前房间时离开的那个房间。
一进入 t 号房间就找到了朋友,立刻停下。
如果在 1 号房间而且没有可以进去的房间,就在没找到朋友的情况下停下。
1 号房间从一开始就算作进去过的房间。
第一行输出找人者穿过门的次数。
进入新房间和往回走都各算一次。
第二行输出找人者待过的房间的编号,从最初的 1 号开始依次列出,用一个空格隔开。
往回走后再次站到的房间,每次都要再写一遍。

输入
第一行给出 n 和 t,用一个空格隔开。
接下来 n 行中的第 i 行给出与 i 号房间相连的房间数 k 和这 k 个房间的编号,用一个空格隔开。
如果没有相连的房间,这一行只有 0。
2 ≤ n ≤ 30,2 ≤ t ≤ n,0 ≤ k ≤ n − 1。
同一行里的房间编号互不相同,不包含自己的编号,并且没有固定的顺序
输出
两行。
第一行是穿过门的次数,第二行是找人者依次待过的房间编号
样例 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 这样,返回来的值为真就立刻返回真,只有为假时才记下“回到了这个房间”。
这个写法在八个解答里都一样(C 用 1 和 0 代替 bool),各语言不同的是怎样把相连的房间按编号排序。
Python sorted(nums)、Java Arrays.sort(int[])、C# Array.Sort(int[])、C++ std::sort、Craykon 列表.排序() 都按数值大小排序。
C 的 qsort 必须传入比较函数。
陷阱一:JS、TS 的 sort() 不传比较函数时,连数字数组也会转成字符串来比较,所以 10 排在 2 前面。
示例 2 中 1 号房间的列表 10, 2 保持原样,找人者先进 10 号房间,第二行变成 1 10 1 2 1。
要写成 sort((a, b) => a - b)。
完全不排序时示例 2 也会同样出错,示例 1 则先进 3 号房间,输出 2 和 1 3 5。
陷阱二:如果不看递归调用返回的值,找到朋友之后找人者还会往回走。
示例 1 第二行的末尾会多出 3 1,第一行变成 8。
陷阱三:如果只记第一次进入的房间(常见的“访问顺序”输出),往回走的步子就漏掉了。
示例 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.连接(" "));