BOJ 9466. 텀 프로젝트
1. 문제설명
- 그래프가 주어진다.
- 그래프는 자기 자신 혹은 다른 노드를 가리키고 있다.
-
이 때 사이클이 형성되면 팀이 되고, 사이클이 형성되지 않으면 팀이 될 수 없다.
- 예제
- 3은 자기 자신을 가리키고 있다.
- 즉, 사이클이 있다.
- 4->7->6->4 순으로 가리키고 있다.
- 즉, 사이클이 있다.
- 그 외에 사이클은 존재하지 않는다.
- 따라서, 팀에 속하지 않은 1, 2, 5 즉 3이 정답이 된다.
- 3은 자기 자신을 가리키고 있다.
2. 알고리즘 설계
- 이 문제의 핵심은 구현이 아니다.
- 한번 들른 곳은 다시 안 들려야 하는 게 관건이다.
- 처음에 DFS로 구현했을 때, 시간 초과를 당했다.
3. 로직
- 방문했음을 알리는 visit 배열을 사용
- 이는 각 노드를 돌 때마다 갱신시켜줘야 한다.
- move 배열
- 이미 들른 적이 있는 노드인가?
4. 전체 코드
#include<iostream>
using namespace std;
const int sz = 100002;
int arr[sz], _move[sz], visit[sz];
int search(int start)
{
int idx = start;
int cnt = 1;
while (true) {
if (_move[idx]) { // 방문한 적이 있다면,
if (start != visit[idx]) // 시작과 같지 않으면, 사이클이 없는 것
return 0;
return cnt - _move[idx]; // 사이클의 수를 계산하여 리턴
}
// 방문하지 않은 노드라면, 갱신
_move[idx] = cnt++;
visit[idx] = start;
idx = arr[idx];
}
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(NULL);
int tc; cin >> tc;
while (tc--) {
int n, result = 0;
cin >> n;
for (int i = 1; i <= n; i++) {
cin >> arr[i];
_move[i] = 0;
visit[i] = 0;
}
for (int i = 1; i <= n; i++)
if (!_move[i])
result += search(i);
cout << n - result << '\n';
}
return 0;
}
5. 소감
- 시간 초과 때문에 진짜 엄청 고민했던 문제
- 처음 구현에는
move
배열 없이 구현하려고 했는데, - 이게 시간을 잡아먹은 주요 원인이었다…