# # # 9466 텀 프로젝트 import sys sys.setrecursionlimit(100000) def dfs(node): global result visited[node] = False total.append(node) next_node = graph[node] if not visited[next_node]: if next_node in total: find_index = total.index(next_node) result +=total[find_index:] return else: dfs(next_node) for _ in range(int(input())): N = int(input()) graph = [0]+list(map(int,sys.stdin.readline().split())) visited = [True] *(N+1) result = [] for ind in range(1,N+1): if visited[ind]: total = [] dfs(ind) print(N-len(result))
문제를 풀때, 어떻게 풀어야할지 고민을 했던 문제이다. 문제를 읽어보면, 사이클을 이루는 경우에만, 팀이 될수 있다는 것을 알 수 있다.
그래서 DFS 재귀를 통해서, 내가 방문한 곳을 다시 방문하게 된다면, 사이클을 이루게 되는 것이기 때문에, 결과값에 추가해주었다. 중간의 find_index 같은 경우 사이클을 이루어지는 최초지점을 찾아서 저장해주기 위함이다.
만약 1,2,3,4,5 가 있고, 각각 2,3,4,5,2를 선택했다고 하면 dfs를 했을때 total에는 [1,2,3,4,5]가 저장되어있을 것이다. 하지만, 1은 문제에 주어진 조건을 만족하지 못하고, 팀이 되지 못한다. 그래서 사이클이 최초로 시작되는 2를 기준으로 잘라주기 위함이다.
for _ in range(int(input())): N = int(input()) graph = list(map(int,input().split())) visited = [0]*N cnt = 0 for current_node in range(N): if not visited[current_node]: next_node = current_node while visited[next_node] == 0: visited[next_node] = 1 next_node = graph[next_node] - 1 cycle_index = current_node while cycle_index != next_node: cnt += 1 cycle_index = graph[cycle_index] - 1 print(cnt)
좀 더 깔끔한 풀이다. 위와 비슷하지만, 최초로 사이클이 시작되는 노드는 중간의 while문을 통과하고 나온 next_node일것이다.
그러면 처음 노드인 current_node를 복사해, cycle_index라고 하고, 이 cycle_index가 next_node와 같지 못하면, 사이클에 포함되지 않는 노드임을 알 수 있다. 이 next_node는 사이클이 최초로 발생하는 노드의 위치이기 때문에, 만약 current_node에서 사이클이 시작됬다면 cycle_node와 next_node는 동일할 것이다.
그래서, 이 cycle_node를 똑같이 반복문을 돌리면서 next_node와 같아질때까지 반복을 한다. 그게 곧 팀에 포함되지 않는 사람의 수를 나타내는 것임을 알 수 있다.
'알고리즘 > 백준' 카테고리의 다른 글
[BOJ/백준] 16946 벽 부수고 이동하기 4 (0) | 2021.02.16 |
---|---|
[BOJ/백준] 1937 욕심쟁이 판다 (0) | 2021.02.16 |
[BOJ/백준] 1759 암호 만들기 (0) | 2021.02.15 |
[BOJ/백준] 1991 트리 순회 (0) | 2021.02.15 |
[BOJ/백준] 11047 동전 0 (0) | 2021.02.15 |