[백준] 24479 알고리즘 수업 - 깊이 우선 탐색 1

J. Hwang·2024년 10월 31일
0

coding test

목록 보기
31/108

문제

오늘도 서준이는 깊이 우선 탐색(DFS) 수업 조교를 하고 있다. 아빠가 수업한 내용을 학생들이 잘 이해했는지 문제를 통해서 확인해보자.

N개의 정점과 M개의 간선으로 구성된 무방향 그래프(undirected graph)가 주어진다. 정점 번호는 1번부터 N번이고 모든 간선의 가중치는 1이다. 정점 R에서 시작하여 깊이 우선 탐색으로 노드를 방문할 경우 노드의 방문 순서를 출력하자.

깊이 우선 탐색 의사 코드는 다음과 같다. 인접 정점은 오름차순으로 방문한다.

dfs(V, E, R) {  # V : 정점 집합, E : 간선 집합, R : 시작 정점
    visited[R] <- YES;  # 시작 정점 R을 방문 했다고 표시한다.
    for each x ∈ E(R)  # E(R) : 정점 R의 인접 정점 집합.(정점 번호를 오름차순으로 방문한다)
        if (visited[x] = NO) then dfs(V, E, x);
}

입력

첫째 줄에 정점의 수 N (5 ≤ N ≤ 100,000), 간선의 수 M (1 ≤ M ≤ 200,000), 시작 정점 R (1 ≤ R ≤ N)이 주어진다.

다음 M개 줄에 간선 정보 u v가 주어지며 정점 u와 정점 v의 가중치 1인 양방향 간선을 나타낸다. (1 ≤ u < v ≤ N, u ≠ v) 모든 간선의 (u, v) 쌍의 값은 서로 다르다.


출력

첫째 줄부터 N개의 줄에 정수를 한 개씩 출력한다. i번째 줄에는 정점 i의 방문 순서를 출력한다. 시작 정점의 방문 순서는 1이다. 시작 정점에서 방문할 수 없는 경우 0을 출력한다.


내 풀이

import sys
sys.setrecursionlimit(10 ** 6)
input = sys.stdin.readline

no_points, no_lines, start = map(int, input().split())

graph = [[] for _ in range(no_points+1)]
visited = [0]*(no_points+1)    # 0이면 방문하지 않음

for i in range(no_lines):
    a, b = map(int, input().split())
    graph[a].append(b)
    graph[b].append(a)
    
# sort by ascending order
for i in range(no_points+1):
    graph[i].sort()

c = 1
def DFS(graph, start, visited):
    global c
    visited[start] = c
    
    for i in graph[start]:
        if visited[i] == 0:
            c += 1
            DFS(graph, i, visited)   # recursive DFS

DFS(graph, start, visited)

for i in range(1, no_points+1):
    print(visited[i])

코멘트

이 문제는 제목에도 나와있듯 깊이 우선 탐색 (DFS: Depth-First Search) 유형의 문제이다. 그래프를 탐색하는 유형인데, 모든 노드를 방문하는데 깊이 순으로 먼저 탐색을 하고, 더 이상 내려갈 곳이 없으면 옆으로 이동한다. 스택이나 재귀함수를 이용하여 구현할 수 있다고 한다.

나는 DFS 알고리즘에 대한 이해가 없어서 이 문제를 어떻게 구현해야할지 1시간 이상 고민해봐도 잘 모르겠어서, 결국 제출하지 못하고 DFS에 대한 포스팅과 해당 문제의 다른 풀이를 보고 나서야 겨우 작성해서 제출할 수 있었다. 다만 다른 풀이와 거의 같은 방식으로 풀었는데도 런타임 에러(RecursionError) 가 떠서, 추후에 답안을 수정해서 제출해야 한다.

+++++++++++++ 런타임 에러 해결 +++++++++++++

import sys
sys.setrecursionlimit(10 ** 6)
input = sys.stdin.readline

기본적으로 파이썬의 재귀 깊이 제한은 약 1000번으로 설정되어 있는데, 위 코드를 이용하면 재귀 깊이를 10610^6번까지 늘려주기 때문에 RecursionError를 방지할 수 있다. 특히나 DFS처럼 재귀를 사용하는 알고리즘에서 유용하다.
또한 sys.stdin.readlineinput()보다 빠르게 입력을 받아들이기 때문에 많은 양의 입력을 받는 경우 효율적이다.
따라서 앞으로도 위 코드를 적극 활용하여 실행 시간을 줄여보자!
++++++++++++++++++++++++++++++++++++++

일단 내가 이해한 바를 정리해서 적어보면, 그래프 탐색 유형에서는 우선 그래프와 (필요하다면) 방문 이력을 기록하는 리스트를 미리 만들어두고, 방문하면 count를 올리고 그래프를 계속 탐색하는 재귀 함수를 작성해야 한다는 것이다.

global에 대해서도 배웠는데, 함수 안에서 변수를 작성하면 그 변수는 내부에서만 사용되고 외부에는 영향을 미치지 않는데, global을 선언하면 함수 외부의 변수를 함수 내부에서 사용할 수 있게 된다고 한다.

그래프 탐색 유형은 너무나 생소하고 아직도 이해가 덜 되어서, 더 많이 풀고 고민해야겠다.


References

https://www.acmicpc.net/problem/24479
https://codingopera.tistory.com/67
https://jeinalog.tistory.com/18
https://dduniverse.tistory.com/entry/백준-24479-깊이-우선-탐색-1-파이썬-python
https://coooco.tistory.com/57

profile
Let it code

0개의 댓글