BOJ 15591 MooTube (Silver)
https://www.acmicpc.net/problem/15591
문제
농부 존은 남는 시간에 MooTube라 불리는 동영상 공유 서비스를 만들었다. MooTube에서 농부 존의 소들은 재밌는 동영상들을 서로 공유할 수 있다. 소들은 MooTube에 1부터 N까지 번호가 붙여진 N (1 ≤ N ≤ 5,000)개의 동영상을 이미 올려 놓았다. 하지만, 존은 아직 어떻게 하면 소들이 그들이 좋아할 만한 새 동영상을 찾을 수 있을지 괜찮은 방법을 떠올리지 못했다.
농부 존은 모든 MooTube 동영상에 대해 “연관 동영상” 리스트를 만들기로 했다. 이렇게 하면 소들은 지금 보고 있는 동영상과 연관성이 높은 동영상을 추천 받을 수 있을 것이다.
존은 두 동영상이 서로 얼마나 가까운 지를 측정하는 단위인 “USADO”를 만들었다. 존은 N-1개의 동영상 쌍을 골라서 직접 두 쌍의 USADO를 계산했다. 그 다음에 존은 이 동영상들을 네트워크 구조로 바꿔서, 각 동영상을 정점으로 나타내기로 했다. 또 존은 동영상들의 연결 구조를 서로 연결되어 있는 N-1개의 동영상 쌍으로 나타내었다. 좀 더 쉽게 말해서, 존은 N-1개의 동영상 쌍을 골라서 어떤 동영상에서 다른 동영상으로 가는 경로가 반드시 하나 존재하도록 했다. 존은 임의의 두 쌍 사이의 동영상의 USADO를 그 경로의 모든 연결들의 USADO 중 최솟값으로 하기로 했다.
존은 어떤 주어진 MooTube 동영상에 대해, 값 K를 정해서 그 동영상과 USADO가 K 이상인 모든 동영상이 추천되도록 할 것이다. 하지만 존은 너무 많은 동영상이 추천되면 소들이 일하는 것이 방해될까 봐 걱정하고 있다! 그래서 그는 K를 적절한 값으로 결정하려고 한다. 농부 존은 어떤 K 값에 대한 추천 동영상의 개수를 묻는 질문 여러 개에 당신이 대답해주기를 바란다.
입력
입력의 첫 번째 줄에는 N과 Q가 주어진다. (1 ≤ Q ≤ 5,000)
다음 N-1개의 줄에는 농부 존이 직접 잰 두 동영상 쌍의 USADO가 한 줄에 하나씩 주어진다. 각 줄은 세 정수 $p_i, q_i, r_i (1 ≤ p_i, q_i ≤ N, 1 ≤ r_i ≤ 1,000,000,000)$를 포함하는데, 이는 동영상 $p_i$와 $q_i$가 USADO $r_i$로 서로 연결되어 있음을 뜻한다.
다음 Q개의 줄에는 농부 존의 Q개의 질문이 주어진다. 각 줄은 두 정수 $k_i$와 $v_i(1 ≤ k_i ≤ 1,000,000,000, 1 ≤ v_i ≤ N)$을 포함하는데, 이는 존의 i번째 질문이 만약 K = $k_i$라면 동영상 $v_i$를 보고 있는 소들에게 몇 개의 동영상이 추천될 지 묻는 것이라는 것을 뜻한다.
출력
Q개의 줄을 출력한다. i번째 줄에는 농부 존의 i번째 질문에 대한 답변이 출력되어야 한다.
예제 입력 1
4 3 1 2 3 2 3 2 2 4 4 1 2 4 1 3 1
예제 출력 1
3 0 2
힌트
농부 존은 1번 동영상과 2번 동영상이 USADO 3을 가지고, 2번 동영상과 3번 동영상이 USADO 2를 가지고, 2번 동영상과 4번 동영상이 USADO 4를 가진다고 했다. 이것에 기반해서 1번 동영상과 3번 동영상의 USADO는 min(3,2)=2가 되고, 1번 동영상과 4번 동영상의 USADO는 min(3,4)=3이 되고, 3번 동영상과 4번 동영상의 USADO는 min(2,4)=2가 된다.
농부 존은 K=1일 때 2번 동영상, K=3일 때 1번 동영상, K=4일 때 1번 동영상을 보면 각각 몇 개의 동영상이 추천될까 궁금해하고 있다. K=1일 때 2번 동영상에서 추천되는 동영상은 1, 3, 4번 동영상이다. K=4일 때 1번 동영상으로부터 추천되는 동영상은 없다. 그러나 K=3일때는 1번 동영상에서 2번 동영상과 4번 동영상이 추천된다.
코드
문제가 정말 이해하기 힘들게 설명해놓은 것 같다.
힌트 부분을 읽고 간신히 이해했다.
문제 설명은 복잡했지만, 그냥 BFS 문제였다.
문제 풀이 과정은 다음과 같다.
- 모든 동영상 사이의 USADO를 BFS로 구한다.
- Q개의 질문을 받으며 동영상 V와 다른 모든 비디오 간의 USADO 중 K 이상인 USADO의 수를 세고, 그 수를 출력한다.
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
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.*;
public class Main {
static class Edge {
int end;
int usado;
public Edge(int end, int usado) {
this.end = end;
this.usado = usado;
}
}
static int N;
static int[][] usados;
static List<Edge>[] edges;
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
StringTokenizer st = new StringTokenizer(br.readLine());
N = Integer.parseInt(st.nextToken());
int Q = Integer.parseInt(st.nextToken());
usados = new int[N][N];
for (int i = 0; i < N; i++) {
Arrays.fill(usados[i], Integer.MAX_VALUE);
}
edges = new List[N];
for (int i = 0; i < N; i++) {
edges[i] = new ArrayList<>();
}
for (int i = 1; i < N; i++) {
st = new StringTokenizer(br.readLine());
int p = Integer.parseInt(st.nextToken()) - 1;
int q = Integer.parseInt(st.nextToken()) - 1;
int r = Integer.parseInt(st.nextToken());
edges[p].add(new Edge(q, r));
edges[q].add(new Edge(p, r));
}
// BFS로 각 비디오마다 모든 비디오와의 usado를 계산
for (int i = 0; i < N; i++) {
calculateUsado(i);
}
// 비디오 V와 다른 모든 비디오 간의 usado 중 K 이상인 usado의 수를 카운트해 출력
StringBuilder answer = new StringBuilder();
for (int i = 0; i < Q; i++) {
st = new StringTokenizer(br.readLine());
int k = Integer.parseInt(st.nextToken());
int v = Integer.parseInt(st.nextToken()) - 1;
int count = 0;
for (int usado: usados[v]) {
if(usado != Integer.MAX_VALUE && usado >= k) {
count++;
}
}
answer.append(count).append('\n');
}
System.out.println(answer);
}
static void calculateUsado(int video) {
boolean[] visited = new boolean[N];
Queue<Integer> queue = new ArrayDeque<>();
queue.offer(video);
visited[video] = true;
while (!queue.isEmpty()) {
int check = queue.poll();
for (Edge edge: edges[check]) {
if(!visited[edge.end]) {
usados[video][edge.end] = Integer.min(usados[video][check], edge.usado);
queue.offer(edge.end);
visited[edge.end] = true;
}
}
}
}
}