문제 풀이

[백준] 9466번 텀 프로젝트

컴영 2020. 4. 2. 00:05

9466 텀 프로젝트


문제


이번 가을학기에 '문제 해결' 강의를 신청한 학생들은 텀 프로젝트를 수행해야 한다. 프로젝트 팀원 수에는 제한이 없다. 심지어 모든 학생들이 동일한 팀의 팀원인 경우와 같이 한 팀만 있을 수도 있다. 프로젝트 팀을 구성하기 위해, 모든 학생들은 프로젝트를 함께하고 싶은 학생을 선택해야 한다. (단, 단 한 명만 선택할 수 있다.) 혼자 하고 싶어하는 학생은 자기 자신을 선택하는 것도 가능하다.

학생들이(s1, s2, ..., sr)이라 할 때, r=1이고 s1이 s1을 선택하는 경우나, s1이 s2를 선택하고, s2가 s3를 선택하고,..., sr-1이 sr을 선택하고, sr이 s1을 선택하는 경우에만 한 팀이 될 수 있다.

주어진 선택의 결과를 보고 어느 프로젝트 팀에도 속하지 않는 학생들의 수를 계산하는 프로그램을 작성하라.


입력


첫째 줄에 테스트 케이스의 개수 T가 주어진다. 각 테스트 케이스의 첫 줄에는 학생의 수가 정수 n (2 ≤ n ≤ 100,000)으로 주어진다. 각 테스트 케이스의 둘째 줄에는 선택된 학생들의 번호가 주어진다. (모든 학생들은 1부터 n까지 번호가 부여된다.)


출력


테스트 케이스마다 한 줄에 출력하고, 각 줄에는 프로젝트 팀에 속하지 못한 학생들의 수를 나타내면 된다.


풀이)


재귀적 방법을 통해 사이클이 생겼으면 팀으로 묶는 방법을 사용했다.

처음에 main()에서 각 학생에 대한 재귀 함수를 호출할 때, 학생이 이미 사이클에 속하는지 아닌지 확인했던 걸 체크해주지 않았더니

각 학생마다 재귀를 돌면서, 시간초과가 일어났다.

그래서 이미 확인해봤던 학생은 재귀를 돌지 않게 하면서, 다시 확인하지 않도록 해줬다.


과정은 이러하다.

1. 빈 vector 변수를 만든다.

2. i*번 학생부터 함수를 통해 함께하고 싶은 학생을 확인한다.

3. 만약 함께하고 싶은 학생이 j일때

   3.1 j가 이전에 확인했던 학생이 아닌 경우 vector에 넣어주고 j에 대한 재귀적 함수 호출한다. 2

   3.2 j가 확인했던 친구라면 vector에서 함수를 처음에 호출했던 학생 번호 i*로 사이클이 생기는 지 확인한다.

        사이클 생성되었다면, vector에서 사이클에 포함된 학생을 제외한 

        나머지 학생의 수를 어느 프로젝트 팀에도 속하지 않는 학생이라고 본다.


코드)


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
#include <stdio.h>
#include <vector>
using namespace std;
 
int n;
vector <int> stu;
bool visited[100001]; //0, 1~100000
vector <int> test;
int person;//팀에 속해져있는사람수
 
void dfs(int idx) {
    if (visited[idx] == false) {    
        test.push_back(idx);
        visited[idx] = true;
        dfs(stu[idx]);
    }
    else {
        int i = 0;
        for (i; i < test.size(); i++) {
            if (test[i] == idx) break;
        }
        person += (test.size() - i);
    }
    
    
}
 
int main() {
    int t;
    scanf("%d"&t);
    for (int i = 0; i < t; i++) {
        scanf("%d"&n);
        stu.resize(n + 1);
        for (int j = 1; j <= n; j++) {
            scanf("%d"&stu[j]);
        }
        
        for (int j = 1; j <= n; j++) {
            if (visited[j] == false) {
                dfs(j);
                test.clear();
            } 
        }
        
        printf("%d\n", n - person);
 
        fill(visited, visited + n + 1false);
        person = 0;
    }
    return 0;
}
cs


혹시 이해가 되지 않을까봐 문제 예시에 대한 그림을 그려봤다.

7명의 학생이 있고, 선택의 결과가 이러하다면


1234567
3137346



결과)