📚 오늘의 문제
링크 : https://www.acmicpc.net/problem/9466
문제 설명
이번 가을학기에 '문제 해결' 강의를 신청한 학생들은 텀 프로젝트를 수행해야 한다. 프로젝트 팀원 수에는 제한이 없다. 심지어 모든 학생들이 동일한 팀의 팀원인 경우와 같이 한 팀만 있을 수도 있다. 프로젝트 팀을 구성하기 위해, 모든 학생들은 프로젝트를 함께하고 싶은 학생을 선택해야 한다. (단, 단 한 명만 선택할 수 있다.) 혼자 하고 싶어하는 학생은 자기 자신을 선택하는 것도 가능하다.
학생들이(s1, s2, ..., sr)이라 할 때, r=1이고 s1이 s1을 선택하는 경우나, s1이 s2를 선택하고, s2가 s3를 선택하고,..., sr-1이 sr을 선택하고, sr이 s1을 선택하는 경우에만 한 팀이 될 수 있다.
예를 들어, 한 반에 7명의 학생이 있다고 하자. 학생들을 1번부터 7번으로 표현할 때, 선택의 결과는 다음과 같다.

위의 결과를 통해 (3)과 (4, 7, 6)이 팀을 이룰 수 있다. 1, 2, 5는 어느 팀에도 속하지 않는다.
주어진 선택의 결과를 보고 어느 프로젝트 팀에도 속하지 않는 학생들의 수를 계산하는 프로그램을 작성하라.
입력
첫째 줄에 테스트 케이스의 개수 T가 주어진다. 각 테스트 케이스의 첫 줄에는 학생의 수가 정수 n (2 ≤ n ≤ 100,000)으로 주어진다. 각 테스트 케이스의 둘째 줄에는 선택된 학생들의 번호가 주어진다. (모든 학생들은 1부터 n까지 번호가 부여된다.)
출력
각 테스트 케이스마다 한 줄에 출력하고, 각 줄에는 프로젝트 팀에 속하지 못한 학생들의 수를 나타내면 된다.
예제 입력 1
2
7
3 1 3 7 3 4 6
8
1 2 3 4 5 6 7 8
예제 출력 1
3
0
🔑 키워드.
Cycle이 발생한다 ⇒ 즉 평범한 DFS , BFS로 접근할 수 없다.
문제 풀이
처음에는 Cycle이 발생하는 것을 확인해야하는 문제인가 싶어 유니온 파인드를 생각했다. 하지만 곧바로 실패 . . 유니온 파인드에 대해 자세히 알지 못하기 때문에 구현도 못하고 포기해버렸다.
일단 나는 이 문제가 DFS로 풀든 BFS로 풀든 같은 플로우가 나온다고 생각했다. 왜냐하면 부모노드(선택하는 사람) 와 자식노드(선택 받는 사람)이 한 쌍이기 때문에 '큐'를 구현해서 풀거나 '스택'을 구현해서 푸나 방문하는 노드의 순서는 같기 때문이다.
그래서 나는 일단 BFS로 풀기로 결정했다.
1
7
2 3 4 5 3 1 2
위와 같은 예시가 있다고 가정하자.
index 1부터 시작한다면
1 → 2 → 3 → 4 → 5 → 3
빨간 색 부분에서 싸이클이 발생한다. 이미 방문했던 곳을 방문하게 되면 Cycle이 발생 했다는 것이다. 여기서 나는 방문한 부분을 한번 더 방문해서 그룹으로 묶어줄 수 있지 않을까? 라는 생각을 했다.
1 → 2 → 3 → 4 → 5 → 3 → 4 → 5 → 3
1, 2, 3 ,4 ,5를 방문하면서 visited에 기록하고
다시 3, 4, 5 를 방문하면 Cycle이 발생했다는 뜻이므로 finished에 기록했다.
다시 방문할 때 count++ 을 진행한다. 여기서 count는 Cycle이 발생한 조원, 즉 조를 구성하게 된 학생들의 수를 의미한다.
그리고 다시 finished 상태인 3을 방문하게 되면 탐색을 종료하면 된다.
1 → 2 → 3 → 4 → 5 → 3 → 4 → 5 → 3
이 프로세스를 visited와 finished 배열이 업데이트되는 과정과 함께 봐보자.
1 → 2 → 3 → 4 → 5
visited [1, 1, 1, 1, 1, 0, 0]
finished [0, 0, 0, 0, 0, 0, 0]
→ 3 → 4 → 5
visited [1, 1, 1, 1, 1, 0, 0]
finished [0, 0, 1, 1, 1, 0, 0]
→ 3
visited [1, 1, 1, 1, 1, 0, 0]
finished [0, 0, 1, 1, 1, 0, 0]
visited[3] , finished[3] 둘다 1이므로 탐색 종료.
오케이! 인덱스 1 기준으로 시작한 cycle은 검출했다. 이제 인덱스 2 기준으로 시작해봐야겠지?
인덱스 2 기준으로 이미 방문한 기록이 있다면, 이전에서 cycle 검출이 끝났다는 의미로 finished에 업데이트해주고 스킵해주면 된다.
visited [1, 1, 1, 1, 1, 0, 0]
finished [0, 1, 1, 1, 1, 0, 0]
하지만 문제는 인덱스 6에서 발생한다.
6 → 1
visited [1, 1, 1, 1, 1, 1, 0]
finished [0, 1, 1, 1, 1, 0, 0]
어? visited[1]이 있으니깐 cycle이 발생하네? finished에 업데이트 하면서 count++을 진행하자!
인덱스 1에서 방문했던 기록과 인덱스 6에서 방문한 기록이 구분이 되지 않으면서 Cycle로 가정하게 되는 문제점이 생기는 것이다. 하 . . .
그래서 나는 visited에 1말고 해당 인덱스로 업데이트 하기로 결정했다.
인덱스 1에서 탐색 시
1 → 2 → 3 → 4 → 5
visited [1, 1, 1, 1, 1, 0, 0]
finished [0, 1, 1, 1, 1, 0, 0]
인덱스 6에서 탐색 시
6 → 1
visited [1, 1, 1, 1, 1, 6, 0]
finished [0, 1, 1, 1, 1, 0, 0]
어? visited에 업데이트 된 숫자가 현재 내 인덱스랑 다르네? 아 ~ 내가 탐색한 부분이 아니구나~ → 종료
이런식으로 구현했다.
결과적으로 내가 구해야하는 것은 조를 이루지 못한 학생의 수를 구하는 것이므로
전체 학생의 수 - count(조를 구성한 학생의 수)를 출력했다.
package week8;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayDeque;
import java.util.Deque;
import java.util.StringTokenizer;
public class boj9466 {
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
int T = Integer.parseInt(br.readLine());
for(int t = 0 ; t<T;t++){
int N = Integer.parseInt(br.readLine());
int[] arr = new int[N+1];
StringTokenizer st = new StringTokenizer(br.readLine());
for(int i =1 ; i<=N; i++){
arr[i] = Integer.parseInt(st.nextToken());
}
int[] visited = new int[N+1];
int[] finished = new int[N+1];
Deque<Integer> queue = new ArrayDeque<>();
int count = 0;
for(int i = 1; i<=N; i++){
if(visited[i]!=0){
finished[i]= 1;
continue;
}
queue.addLast(i);
while(!queue.isEmpty()){
int now = queue.pollFirst();
if(finished[now] == 1){
continue;
}
if(visited[now] == 0){
visited[now] = i;
}else if(visited[now]==i){
finished[now] = 1;
count ++;
}else{
finished[now] = 1;
}
queue.addLast(arr[now]);
}
}
System.out.println(N-count);
}
}
}'알고리즘' 카테고리의 다른 글
| [JAVA] 백준1509번 "팰린드롬 분할" (0) | 2025.05.09 |
|---|---|
| [JAVA] 백준 7795번 "먹을 것인가 먹힐 것인가" (0) | 2024.07.16 |
| [JAVA] 0x08 백준 2504번 "괄호의 값" (1) | 2024.06.26 |
| [JAVA] 0x07 백준 11003번 "최솟값 찾기" (2) | 2024.06.19 |
| [Java] Linked List와 List Iterator (0) | 2024.06.03 |