[BOJ] Q2150 Strongly Connected Component
[BOJ] Q2150 Strongly Connected Component
Question
Language: Python
Difficulty: Platinum 5
해당 문제는 방향 그래프에서의 SCC을 찾는 문제의 유형이다. SCC란, 임의 노드 u,v 에 대해 u->v, v->u에 대한 경로가 모두 존재하는 노드들에 대한 집합을 의미한다. SCC을 구하는 알고리즘으로는 크게 2가지가 있는데, Kosaraju, Tarjan이 존재한다.
Kosaraju
- DFS을 수행하여, dfs 순서를 스택에 저장한다.
- 스택에 저장된 노드에 대해서 한 개씩 pop 해서 역방향 그래프에 대해서 dfs을 수행한다.
- 역방향 그래프에 대한 dfs 수행과정에서 SCC가 각각 구해지게 된다.
import sys
#특정 그래프에 대한 dfs 순회
def dfs(graph,vertex):
global dfs_stack
if visited[vertex]:
return
visited[vertex]=True
for adj_vertex in graph[vertex]:
dfs(graph,adj_vertex)
dfs_stack.append(vertex)
def solution():
global visited,dfs_stack
#dfs 수행
for vertex in range(1,n_vertex+1):
if not visited[vertex]:
dfs(graph,vertex)
#dfs 순서를 stack에 저장한다.
stack=dfs_stack[:]
visited=[False]*(n_vertex+1)
components=[]
#역방향 그래프에 대한 dfs 수행을 통해 SCC을 찾는다.
while stack:
vertex=stack.pop()
if visited[vertex]:
continue
dfs_stack=[]
dfs(rev_graph,vertex)
#각각의 vertex에 대해 역방향 그래프의 dfs 순회를 진행해서 SCC를 구한다.
components.append(sorted(dfs_stack))
components.sort()
print(len(components))
for component in components:
for vertex in component:
print(vertex,end=" ")
print(-1)
if __name__ == "__main__":
sys.setrecursionlimit(10**6)
n_vertex,n_edge=map(int,input().split())
graph=[[] for _ in range(n_vertex+1)]
rev_graph=[[] for _ in range(n_vertex+1)]
for _ in range(n_edge):
src,des=map(int,input().split())
graph[src].append(des)
rev_graph[des].append(src)
dfs_stack=[]
visited=[False] * (n_vertex+1)
solution()
Tarjan
코사라주 방식과 달리 타잔 알고리즘은 한번의 dfs 과정을 통해 SCC를 구하게 된다. 자세한 타잔 알고리즘의 동작 방식은 위의 블로그를 참고하면 된다.
타잔 알고리즘의 동작과정을 요약하면 아래와 같다
- 각각의 노드에 대해 dfs 순회를 실행한다.
- 노드의 초기 discovered 값은 고유 id값으로 설정하게 된다. 다음 노드에 대해 방문 이력이 없는 경우 다음 노드에 대한 dfs을 진행한다.
- 만일 다음 노드를 이전에 방문 하였으며 처리중인 경우(dfs 순회 중인경우) 다음 노드의 discovered 값(id값)이 새로운 부모값이 된다.
- 이와 같이 dfs을 이어나가다, parent 값이 자기 자신인 경우 스택에서 자기 자신 노드가 나올 때까지 노드를 추출해서 SCC를 구한다.
def dfs(vertex):
global dfs_stack,components,id,finished
#각 노드에 고유 id 값을 부여한다.
discovered[vertex]=id
id+=1
stack.append(vertex)
parent=discovered[vertex]
for adj_vertex in graph[vertex]:
#방문 이력이 없는 경우
if discovered[adj_vertex] == -1:
parent=min(parent,dfs(adj_vertex))
#방문하였지만, 아직 처리 중인 경우
elif not finished[adj_vertex]:
parent=min(parent,discovered[adj_vertex])
#부모노드가 자기 자신과 동일한 경우, 자기 자신 노드가 나올때 까지 스택에서 노드를 빼서 scc를 구한다.
if parent==discovered[vertex]:
component=[]
while stack:
temp=stack.pop()
component.append(temp)
#해당 노드에 대한 dfs 작업은 완료된 상태이므로 처리 상태를 완료로 변경한다.
finished[temp]=True
if temp == vertex:
break
components.append(sorted(component))
return parent
def solution():
#dfs 수행
for vertex in range(1,n_vertex+1):
if discovered[vertex]==-1:
dfs(vertex)
components.sort()
print(len(components))
for component in components:
for vertex in component:
print(vertex,end=" ")
print(-1)
if __name__ == "__main__":
with open("input2150.txt","r") as file:
n_vertex,n_edge=map(int,file.readline().split())
graph=[[] for _ in range(n_vertex+1)]
rev_graph=[[] for _ in range(n_vertex+1)]
for _ in range(n_edge):
src,des=map(int,file.readline().split())
graph[src].append(des)
rev_graph[des].append(src)
id=1
components=[]
stack=[]
discovered=[-1] * (n_vertex+1)
finished=[False]*(n_vertex+1)
solution()
댓글남기기