문제
N개의 도시와 그 도시를 연결하는 N-1개의 도로로 이루어진 도로 네트워크가 있다.
모든 도시의 쌍에는 그 도시를 연결하는 유일한 경로가 있고, 각 도로의 길이는 입력으로 주어진다.
총 K개의 도시 쌍이 주어진다. 이때, 두 도시를 연결하는 경로 상에서 가장 짧은 도로의 길이와 가장 긴 도로의 길이를 구하는 프로그램을 작성하시오.
입력
첫째 줄에 N이 주어진다. (2 ≤ N ≤ 100,000)
다음 N-1개 줄에는 도로를 나타내는 세 정수 A, B, C가 주어진다. A와 B사이에 길이가 C인 도로가 있다는 뜻이다. 도로의 길이는 1,000,000보다 작거나 같은 양의 정수이다.
다음 줄에는 K가 주어진다. (1 ≤ K ≤ 100,000)
다음 K개 줄에는 서로 다른 두 자연수 D와 E가 주어진다. D와 E를 연결하는 경로에서 가장 짧은 도로의 길이와 가장 긴 도로의 길이를 구해서 출력하면 된다.
출력
총 K개 줄에 D와 E를 연결하는 경로에서 가장 짧은 도로의 길이와 가장 긴 도로의 길이를 출력한다.
풀이
LCA를 활용하여 문제를 풀이합니다. 기존 LCA에서는 parents 배열을 아래와 같이 정의했습니다.
parent[n][k] : n번 노드의 2^k 번째 위 조상 노드
마찬가지로 가장 짧은 도로의 길이, 가장 긴 도로의 길이도 dp 배열로 만들어줍니다.
minLength[n][k] : n번 노드에서 2^k번째 위 조상 노드까지 연결하는 경로 중 가장 짧은 도로 길이
maxLength[n][k] : n번 노드에서 2^k번째 위 조상 노드까지 연결하는 경로 중 가장 긴 도로 길이
// 전처리를 수행한다.
// 각 노드의 깊이, 부모를 계산한다.
void preprocessing1(int cur, int depth, int parent, int lengths) {
visited[cur] = true;
depths[cur] = depth;
parents[cur][0] = parent; // parent는 cur노드의 바로 위 부모
maxLength[cur][0] = lengths;
minLength[cur][0] = lengths;
for (int i = 0; i < myGraph[cur].size(); i++) {
int next = myGraph[cur][i];
int length = myCost[cur][i];
if (visited[next] == false) {
preprocessing1(next, depth + 1, cur, length);
}
}
}
먼저 LCA의 전처리 중 각 노드의 깊이, 부모를 계산하는 로직에서
부모까지의 경로를 maxLength[cur][0], minLength[cur][0]에 넣어줍니다.
// 조상 노드를 계산한다.
void preprocessing2() {
for (int j = 1; j < 20; j++) {
for (int i = 1; i <= N; i++) {
parents[i][j] = parents[parents[i][j - 1]][j - 1];
maxLength[i][j] = max(maxLength[i][j - 1], maxLength[parents[i][j - 1]][j - 1]);
minLength[i][j] = min(minLength[i][j - 1], minLength[parents[i][j - 1]][j - 1]);
}
}
}
LCA의 전처리 중 조상 노드를 계산하는 로직에서
maxLength, minLength 배열을 업데이트 해줍니다.
예를 들어 maxLength[9][2]는 9번 노드의 2^2번째 부모까지 연결하는 경로 중 가장 긴 도로의 길이입니다.
LCA 로직해서는 아래 로직을 추가합니다.
while (depths[first] != depths[second]) {
for (int i = 20; i >= 0; i--) {
if (depths[parents[first][i]] >= depths[second]) {
maxAnswer = max(maxAnswer, max(maxAnswer, maxLength[first][i]));
minAnswer = min(minAnswer, min(minAnswer, minLength[first][i]));
first = parents[first][i];
}
}
}
두 노드의 깊이가 달라, 깊이를 맞춰주는 과정에서 maxAnswer, minAnswer를 갱신해줍니다.
// 3. first 노드와 second 노드의 공통 조상이 다르다면 가능한 만큼 거슬러 올라간다.
while (parents[first][0] != parents[second][0]) {
for (int i = 20; i >= 0; i--) {
if (parents[first][i] != parents[second][i]) {
maxAnswer = max(maxAnswer, max(maxLength[first][i], maxLength[second][i]));
minAnswer = min(minAnswer, min(minLength[first][i], minLength[second][i]));
first = parents[first][i];
second = parents[second][i];
}
}
}
두 노드의 부모가 달라서 거슬러 올라가는 과정에서도 maxAnswer, minAnswer를 갱신해줍니다.
maxAnswer = max(maxAnswer, max(maxLength[first][0], maxLength[second][0]));
minAnswer = min(minAnswer, min(minLength[first][0], minLength[second][0]));
마지막으로 두 노드의 부모가 동일해졌을 때 maxAnswer, minAnswer를 갱신해줍니다.
전체 코드는 아래와 같습니다.
#include <iostream>
#include <vector>
using namespace std;
const int MAX = 100010;
vector<int> myGraph[MAX];
vector<int> myCost[MAX];
int N, M;
int depths[MAX];
int parents[MAX][25];
bool visited[MAX] = { false, };
int maxLength[MAX][25]; // i번 노드에서 2^j번째 부모까지 모든 노드 중 최대길이(최댓값)
int minLength[MAX][25]; // i번 노드에서 2^j번째 부모까지 모든 노드 중 최소길이(최솟값)
int minAnswer = 987654321, maxAnswer = 0;
int max(int a, int b) {
if (a > b) return a;
else return b;
}
int min(int a, int b) {
if (a < b) return a;
else return b;
}
// 전처리를 수행한다.
// 각 노드의 깊이, 부모를 계산한다.
void preprocessing1(int cur, int depth, int parent, int lengths) {
visited[cur] = true;
depths[cur] = depth;
parents[cur][0] = parent; // parent는 cur노드의 바로 위 부모
maxLength[cur][0] = lengths;
minLength[cur][0] = lengths;
for (int i = 0; i < myGraph[cur].size(); i++) {
int next = myGraph[cur][i];
int length = myCost[cur][i];
if (visited[next] == false) {
preprocessing1(next, depth + 1, cur, length);
}
}
}
// 조상 노드를 계산한다.
void preprocessing2() {
for (int j = 1; j < 20; j++) {
for (int i = 1; i <= N; i++) {
parents[i][j] = parents[parents[i][j - 1]][j - 1];
maxLength[i][j] = max(maxLength[i][j - 1], maxLength[parents[i][j - 1]][j - 1]);
minLength[i][j] = min(minLength[i][j - 1], minLength[parents[i][j - 1]][j - 1]);
}
}
}
int LCA(int first, int second) {
// 1. 두 노드의 깊이가 다르다면 깊이를 맞춰준다.
if (depths[first] != depths[second]) {
// 편의를 위해 first 노드의 깊이가 더 깊도록 설정한다.
if (depths[first] < depths[second]) {
int tmp = second;
second = first;
first = tmp;
}
while (depths[first] != depths[second]) {
for (int i = 20; i >= 0; i--) {
if (depths[parents[first][i]] >= depths[second]) {
maxAnswer = max(maxAnswer, maxLength[first][i]);
minAnswer = min(minAnswer, minLength[first][i]);
first = parents[first][i];
}
}
}
}
// 2. 두 노드가 같다면 해당 노드가 최소 공통 조상 노드이다.
if (first == second) return first;
// 3. first 노드와 second 노드의 공통 조상이 다르다면 가능한 만큼 거슬러 올라간다.
while (parents[first][0] != parents[second][0]) {
for (int i = 20; i >= 0; i--) {
if (parents[first][i] != parents[second][i]) {
maxAnswer = max(maxAnswer, max(maxLength[first][i], maxLength[second][i]));
minAnswer = min(minAnswer, min(minLength[first][i], minLength[second][i]));
first = parents[first][i];
second = parents[second][i];
}
}
}
maxAnswer = max(maxAnswer, max(maxLength[first][0], maxLength[second][0]));
minAnswer = min(minAnswer, min(minLength[first][0], minLength[second][0]));
return parents[first][0];
}
int main() {
scanf("%d", &N);
for (int i = 0; i < N - 1; i++) {
int first, second, money;
scanf("%d %d %d", &first, &second, &money);
myGraph[first].push_back(second);
myGraph[second].push_back(first);
myCost[first].push_back(money);
myCost[second].push_back(money);
}
for (int p = 0; p < MAX; p++) {
for (int q = 0; q < 25; q++) {
minLength[p][q] = 987654321;
}
}
preprocessing1(1, 0, 0, 0);
preprocessing2();
depths[0] = -1;
scanf("%d", &M);
for (int i = 0; i < M; i++) {
minAnswer = 987654321, maxAnswer = 0;
int first, second;
scanf("%d %d", &first, &second);
int lcaValue = LCA(first, second);
printf("%d %d\n", minAnswer, maxAnswer);
}
return 0;
}
'Algorithm' 카테고리의 다른 글
[C++][백준] 타임머신(11657) - 벨만포드 알고리즘 (0) | 2021.02.26 |
---|---|
[C++][백준] 게임(1072) - 파라메트릭 서치 (0) | 2021.02.26 |
[C++][백준] 홀수 홀릭 호석(20164) - DFS (0) | 2021.02.22 |
[C++][백준] 내려가기(2096) - DP (0) | 2021.02.22 |
[C++][백준] 꿈틀꿈틀 호석 애벌레 - 효율성(20181) - 투포인터, DP (0) | 2021.02.18 |