[BOJ 9466] 텀 프로젝트

joon_1592·2022년 3월 14일
0

알고리즘

목록 보기
31/51

방향그래프에서 사이클에 속하는 정점이 몇개인지 알아야 하는 문제
단순히 사이클이 존재하는지 확인하는 것이라면 union-find를 하겠지만 정확히 누가 사이클의 원소인지 파악해야한다.

문제 예시1번이라면
1에서 dfs 시작
trace가 [1, 3]에서 3을 재방문하므로 사이클인 [3]의 크기는 1 리턴
2에서 dfs 시작
trace가 [2] 1을 이미 방문했으므로 끝, 사이클이 없으므로 0 리턴
3은 이미 방문했으므로 dfs 안함
4에서 dfs 시작
trace가 [4, 7, 6]에서 4를 재방문하므로 사이클인 [4, 7, 6]의 크기 3 리턴
5에서 dfs 시작
trace가 [5]에서 3을 이미 방문했으므로 끝, 사이클이 없으므로 0 리턴
6, 7은 이미 방문했으므로 dfs하지 않는다

import sys
#sys.stdin = open('input.txt', 'r')
sys.setrecursionlimit(int(1e5))
input = sys.stdin.readline

def dfs(graph: dict, visit: list, trace: list, cur: int):
    '''
    graph (dict): 인접리스트로 구현한 그래프
    visit (list): 이미 방문한 정점인지 확인하는 리스트
    trace (list): 현재 방문중인 정점들을 저장하는 리스트
    cur (int): 현재 정점
    '''

    # 방문 확인, 방문 리스트 추가
    visit[cur] = True
    trace.append(cur)
    for node in graph[cur]:
        # 방문하지 않았다면 DFS로 계속 탐색
        if not visit[node]:
            return dfs(graph, visit, trace, node)
        # 방문했는데 그 정점이 현재 방문리스트에 있다면 사이클
        # 사이클에 속하는 원소의 개수를 리턴
        elif node in trace:
            idx = trace.index(node)
            return len(trace[idx:])
    
        # 사이클 없음
    # 따라서 사이클의 원소의 개수는 0
    return 0


T = int(input())
for _ in range(T):
    N = int(input())
    graph = {i: [] for i in range(1, N + 1)}
    pick = list(map(int, input().split()))
    for i, p in enumerate(pick, 1):
        graph[i].append(p)

    answer = 0
    visit = [False] * (N + 1)
    for i in range(1, N + 1):
        if not visit[i]:
            trace = []
            answer += dfs(graph, visit, trace, i)
    print(N - answer)
profile
공부용 벨로그

0개의 댓글