🔗 9466번 : 텀 프로젝트
● Java 풀이
● 시간 제한 3초
● 메모리 제한 256 MB
문제
이번 가을학기에 '문제 해결' 강의를 신청한 학생들은 텀 프로젝트를 수행해야 한다. 프로젝트 팀원 수에는 제한이 없다. 심지어 모든 학생들이 동일한 팀의 팀원인 경우와 같이 한 팀만 있을 수도 있다. 프로젝트 팀을 구성하기 위해, 모든 학생들은 프로젝트를 함께하고 싶은 학생을 선택해야 한다. (단, 단 한 명만 선택할 수 있다.) 혼자 하고 싶어하는 학생은 자기 자신을 선택하는 것도 가능하다.
학생들이(s1, s2, ..., sr)이라 할 때, r=1이고 s1이 s1을 선택하는 경우나, s1이 s2를 선택하고, s2가 s3를 선택하고,..., sr-1이 sr을 선택하고, sr이 s1을 선택하는 경우에만 한 팀이 될 수 있다.
예를 들어, 한 반에 7명의 학생이 있다고 하자. 학생들을 1번부터 7번으로 표현할 때, 선택의 결과는 다음과 같다.
1 | 2 | 3 | 4 | 5 | 6 | 7 |
3 | 1 | 3 | 7 | 3 | 4 | 6 |
위의 결과를 통해 (3)과 (4, 7, 6)이 팀을 이룰 수 있다. 1, 2, 5는 어느 팀에도 속하지 않는다.
주어진 선택의 결과를 보고 어느 프로젝트 팀에도 속하지 않는 학생들의 수를 계산하는 프로그램을 작성하라.
테스트 케이스
/** 출처 (https://www.acmicpc.net/board/view/132105) **/
// input
1
11
2 10 4 5 6 7 8 9 2 2 11
// answer
8
/** 출처 (https://www.acmicpc.net/board/view/129334) **/
// input
4
3
2 3 3
4
2 3 4 2
5
2 3 4 5 4
5
1 1 1 1 1
// answer
2
1
3
4
/** 출처 (https://www.acmicpc.net/board/view/47849) **/
// input
7
6
2 3 4 5 6 2
5
2 5 4 5 2
6
1 3 4 3 2 6
13
2 4 5 2 4 1 8 9 10 11 9 10 10
10
2 5 7 1 6 8 8 3 5 10
10
2 7 10 5 3 3 9 10 6 3
6
2 1 1 2 6 3
// answer
1
3
2
8
6
8
4
풀이
import java.io.*;
import java.util.Stack;
import java.util.StringTokenizer;
public class Main {
private static int N;
private static final int MAX = 100000; // 학생 수 (1부터 시작)
private static int[] chooses, visited;
private static Stack<Integer> stack;
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
StringBuilder sb = new StringBuilder();
StringTokenizer st;
stack = new Stack<>(); // 사이클 확인할 Stack
int IS_NOT_CYCLE; // 사이클에 속하지 않는 노드를 셀 변수
chooses = new int[MAX + 1]; // 선택 학생 담을 배열
visited = new int[MAX + 1]; // 방문 처리 배열
int T = Integer.parseInt(br.readLine()); // 테스트 케이스 개수
for (int tc = 0; tc < T; tc++) {
IS_NOT_CYCLE = 0; // 사이클에 속하지 않는 노드 0으로 초기화
N = Integer.parseInt(br.readLine()); // 학생 수 (노드 수)
st = new StringTokenizer(br.readLine());
for (int i = 1; i <= N; i++) {
chooses[i] = Integer.parseInt(st.nextToken()); // 선택 학생 초기화
visited[i] = (i == chooses[i]) ? i : 0; // 방문 배열 초기화 (자기 자신을 가리키는 학생의 경우 자기순환으로 방문 처리)
}
for (int i = 1; i <= N; i++) {
if (visited[i] == 0) { // 방문하지 않은 노드일 때
if (visited[chooses[i]] != 0) { // 가리키는 노드가 이미 방문한 노드일 때
visited[i] = -1; // 사이클에 속하지 않는 변수임이 확실하므로 -1로 방문 처리 (-1 = 순환하지 않는 노드)
} else { // 가리키는 노드도 방문하지 않았을 때
//System.out.println("==== " + i + "번 노드 시작 ====");
findCycle(i); // 노드 방문 함수 호출
}
}
//System.out.println("방문 여부 : " + Arrays.toString(Arrays.copyOf(visited, N + 1)) + "\n");
if (visited[i] == -1) IS_NOT_CYCLE++; // 순환하지 않는 노드일 경우 + 1
}
sb.append(IS_NOT_CYCLE).append("\n");
}
bw.append(sb.toString());
bw.flush();
bw.close();
br.close();
}
private static void findCycle(int start) { // Stack으로 순환 노드 찾는 함수
while (0 == visited[start]) { // 재방문하는 노드가 나올 때까지
visited[start] = -2; // 방문 처리 (-2는 현재 함수에서의 임의 방문)
stack.add(start); // Stack에 방문 노드 삽입
start = chooses[start]; // 가리키는 노드로 들어가기
}
/*
System.out.println("이전 Stack : " + stack);
System.out.println("방문 여부 : " + Arrays.toString(Arrays.copyOf(visited, N + 1)));
System.out.println("start 값 : " + start);
System.out.println("visited[start] 값 : " + visited[start]);
*/
if (visited[start] == -2) { // 해당 노드에서 재방문 했을 때 (= 사이클 그래프)
while (!stack.isEmpty() && start != stack.peek()) { // 재방문 사이클의 첫 노드가 나올 때까지
visited[stack.pop()] = 1; // Stack에서 순환하는 노드 방문 처리 (1 = 순환하는 노드)
}
if (!stack.isEmpty()) {
visited[stack.pop()] = 1; // 재방문 사이클의 첫 노드 방문 처리
}
}
//System.out.println("이후 Stack 변화 : " + stack);
while (!stack.isEmpty()) {
visited[stack.pop()] = -1; // 사이클에 속하지 않는 노드 모두 -1 방문 처리
}
}
}
만만하게 봤다가 큰 코 다친 문제.
사이클에 포함되지 않은 노드들을 세는 문제이다.
100,000명의 학생 + 테스트케이스로 인해 3초라는 시간이 엄청나게 짧게 느껴졌다.
반례도 여러 개 찾아보느라 작성한 테스트 케이스가 많은 날....ㅎㅎ
DFS로 푼 사람도 있다고 하는데 나는 스택으로 풀었다.
사이클을 찾을 때의 방문 처리와, 반복 작업을 하지 않기 위한 방문 처리를 한 배열에 쓰는 것이 꽤 어려웠다.
계속 방문 배열을 찍어보며 풀었던 너무너무 힘들었던 문제...
'알고리즘 및 데이터 구조 > 백준' 카테고리의 다른 글
[백준] 11066번 : 파일 합치기 (Java) (0) | 2024.02.23 |
---|---|
[백준] 11049번 : 행렬 곱셈 순서 (Java) (0) | 2024.02.23 |
[백준] 14890번 : 경사로 (Java) (0) | 2024.02.20 |
[백준] 1005번 : ACM Craft (Java) (0) | 2024.02.19 |
[백준] 1520번 : 내리막 길 (Java) (0) | 2024.02.18 |