Problem 🔒
문제
지민이는 파티에 가서 이야기 하는 것을 좋아한다. 파티에 갈 때마다, 지민이는 지민이가 가장 좋아하는 이야기를 한다. 지민이는 그 이야기를 말할 때, 있는 그대로 진실로 말하거나 엄청나게 과장해서 말한다. 당연히 과장해서 이야기하는 것이 훨씬 더 재미있기 때문에, 되도록이면 과장해서 이야기하려고 한다. 하지만, 지민이는 거짓말쟁이로 알려지기는 싫어한다. 문제는 몇몇 사람들은 그 이야기의 진실을 안다는 것이다. 따라서 이런 사람들이 파티에 왔을 때는, 지민이는 진실을 이야기할 수 밖에 없다. 당연히, 어떤 사람이 어떤 파티에서는 진실을 듣고, 또다른 파티에서는 과장된 이야기를 들었을 때도 지민이는 거짓말쟁이로 알려지게 된다. 지민이는 이런 일을 모두 피해야 한다.
사람의 수 N이 주어진다. 그리고 그 이야기의 진실을 아는 사람이 주어진다. 그리고 각 파티에 오는 사람들의 번호가 주어진다. 지민이는 모든 파티에 참가해야 한다. 이때, 지민이가 거짓말쟁이로 알려지지 않으면서, 과장된 이야기를 할 수 있는 파티 개수의 최댓값을 구하는 프로그램을 작성하시오.
입력
첫째 줄에 사람의 수 N과 파티의 수 M이 주어진다.
둘째 줄에는 이야기의 진실을 아는 사람의 수와 번호가 주어진다. 진실을 아는 사람의 수가 먼저 주어지고 그 개수만큼 사람들의 번호가 주어진다. 사람들의 번호는 1부터 N까지의 수로 주어진다.
셋째 줄부터 M개의 줄에는 각 파티마다 오는 사람의 수와 번호가 같은 방식으로 주어진다.
N, M은 50 이하의 자연수이고, 진실을 아는 사람의 수는 0 이상 50 이하의 정수, 각 파티마다 오는 사람의 수는 1 이상 50 이하의 정수이다.
출력
첫째 줄에 문제의 정답을 출력한다.
예제 입력 1
4 3
0
2 1 2
1 3
3 2 3 4
예제 출력 1
3
예제 입력 2
4 1
1 1
4 1 2 3 4
예제 출력 2
0
예제 입력 3
4 1
0
4 1 2 3 4
예제 출력 3
1
예제 입력 4
4 5
1 1
1 1
1 2
1 3
1 4
2 4 1
예제 출력 4
2
예제 입력 5
10 9
4 1 2 3 4
2 1 5
2 2 6
1 7
1 8
2 7 8
1 9
1 10
2 3 10
1 4
예제 출력 5
4
예제 입력 6
8 5
3 1 2 7
2 3 4
1 5
2 5 6
2 6 8
1 8
예제 출력 6
5
예제 입력 7
3 4
1 3
1 1
1 2
2 1 2
3 1 2 3
예제 출력 7
0
Approach ⭕
이 문제는 유니온 파이드(Union-Find)를 사용하여 해결해야한다.
- 입력 및 초기화
- 부모노드를 저장할 tree 배열과, 진실을 아는 사람을 표시한 know 배열을 생성한다.
- 진실을 아는 사람을 know 배열에서 true로 표시한다.
- 각 파티를 저장하기 위해 큐를 생성한다. (왜 파티를 저장해?)
- 파티를 2번 확인해야하기 때문이다.
- 먼저 각 파티를 순회하면서, 진실을 아는 사람이 포함된 파티를 확인한다.
- 다시 파티를 순회하면서, 거짓말이 가능한 파티 수를 센다.
- 각 파티 순회
- 각 파티마다 오는 사람을 party 배열에 저장하여 큐에 추가한다.
- 같은 파티에 오는 사람들을 같은 그룹으로 묶는다. (uni(int x, int y) 메서드)
- 진실을 말해야하는 사람 갱신해주기
- 진실을 아는 사람들과 같은 그룹으로 묶인 모든 사람들을 진실을 아는 것으로 설정한다.
- 진실을 아는 사람의 루트 노드를 찾고, know 배열에서 true로 갱신한다.
- 거짓말이 가능한 파티 수 세기
- 큐에서 파티를 하나씩 꺼내어, 파티에 속한 한 사람만 확인해도 거짓말이 가능한 파티인지 알 수 있다.
- 파티에 속한 임의의 한 사람의 루트 노드를 찾는다.
- 루트 노드가 know 배열에서 false라면, 그 파티에는 진실을 아는 사람이 단 한 명도 없다는 걸 의미하므로 개수를 증가시킨다.
알게된 점 😯
아래의 반복문에서 knowCnt가 0이여도 오류가 발생하지 않는다 ㅎㅎ
기초적인 지식이겠지만, i<knowCnt 조건이 거짓이 되어 반복문을 실행하지 않고 넘어간다.
int knowCnt = sc.nextInt();
for(int i=0; i<knowCnt; i++){
int n = sc.nextInt(); // 진실 아는 사람
know[n] = true; // 진실 아는 사람 표시
}
Solution 💡
import java.util.Arrays;
import java.util.LinkedList;
import java.util.Queue;
import java.util.Scanner;
public class 거짓말 {
static int N, M, tree[];
static boolean know[]; // true : 진실을 말해야하는 사람
static Queue<int[]> q = new LinkedList<>();
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
N = sc.nextInt()+1; M = sc.nextInt();
know = new boolean[N];
tree = new int[N];
Arrays.fill(tree, -1);
int knowCnt = sc.nextInt();
for(int i=0; i<knowCnt; i++){
int n = sc.nextInt(); // 진실 아는 사람
know[n] = true; // 진실 아는 사람 표시
}
// 파티의 수 만큼 반복
for(int i=0; i<M; i++){
int partyNum = sc.nextInt(); // 파티마다 오는 사람 수
int[] party = new int[partyNum]; // 파티마다 오는 사람 저장
for(int j=0; j<partyNum; j++){
party[j] = sc.nextInt();
}
q.add(party); // 큐에 파티 추가
// 같은 파티에 오는 사람들(2명이상이면) 그룹으로 묶기
for(int j=1; j<partyNum; j++){
uni(party[0], party[j]);
}
}
// 진실을 말해야하는 사람 갱신해주기
for(int i=1; i<N; i++){
// 진실을 아는 사람의 루트 노드를 찾고,
// 루트 노드를 true로 갱신한다.
if(know[i]) know[find(i)] = true;
}
// 큐에서 파티를 꺼내면서 파티 개수 구하기
int max = 0;
while (!q.isEmpty()){
int[] get = q.poll();
if(!know[find(get[0])]) max++;
}
System.out.println(max);
}
public static void uni(int x, int y){
x = find(x);
y = find(y);
if (x != y) tree[y] = x;
}
public static int find(int x){
if(tree[x]<0) return x;
return tree[x] = find(tree[x]); // 경로 압축 최적화
}
}
'코딩 테스트(Coding Test) > 백준' 카테고리의 다른 글
[백준] 17835번 : 면접보는 승범이네 - 자바(Java) (0) | 2024.12.01 |
---|---|
[백준] 16946번 : 벽 부수고 이동하기 4 - 자바(Java) (1) | 2024.11.30 |
[백준] 1922번 : 네트워크 연결 - 자바(Java) (0) | 2024.11.27 |
[백준] 1717번 : 집합의 표현 - 자바(Java) (0) | 2024.11.27 |
[백준] 1261번 : 알고스팟 - 자바(Java) (0) | 2024.11.22 |