#1240 노드사이의 거리
난이도 : 골드 5
유형 : 트리 / DFS
▸ 문제
N(2≤N≤1,000)개의 노드로 이루어진 트리가 주어지고 M(M≤1,000)개의 두 노드 쌍을 입력받을 때 두 노드 사이의 거리를 출력하라.
▸ 입력
첫째 줄에 노드의 개수 N이 입력되고 다음 N-1개의 줄에 트리 상에 연결된 두 점과 거리(10,000 이하의 정수)를 입력받는다. 그 다음 줄에는 거리를 알고 싶은 M개의 노드 쌍이 한 줄에 한 쌍씩 입력된다.
▸ 출력
M개의 줄에 차례대로 입력받은 두 노드 사이의 거리를 출력한다.
문제 풀이
트리의 크기가 크게 주어졌다면 LCA 알고리즘을 사용하여 쿼리 최적화를 해주면되지만 해당 문제는 쿼리의 수랑 트리 크기가 작으므로 그냥 DFS탐색으로 두 노드 사이의 거리를 구해주면 된다.
설계
- 노드 데이터와 거리 비용을 인접리스트 배열에 저장한다. list[u].add(new Node(v,w));
- dfs탐색을 통해 to노드부터 from노드까지의 이동 거리를 구한다.
- if(to == from) answer = cost;
풀이 코드
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;
import java.util.StringTokenizer;
public class Main {
static class Node{
int to;
int cost;
public Node(int to, int cost) {
this.to = to;
this.cost = cost;
}
}
static int answer;
static List<Node>[] list;
static int[] cost;
public static void main(String[] args) throws IOException{
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
StringTokenizer st = new StringTokenizer(br.readLine());
int n = Integer.parseInt(st.nextToken());
int m = Integer.parseInt(st.nextToken());
list = new ArrayList[n+1];
cost = new int[n+1];
for(int i=1; i<n+1; i++) {
list[i] = new ArrayList<>();
}
for(int i=0; i<n-1; i++) {
st = new StringTokenizer(br.readLine());
int u = Integer.parseInt(st.nextToken());
int v = Integer.parseInt(st.nextToken());
int w = Integer.parseInt(st.nextToken());
list[u].add(new Node(v,w));
list[v].add(new Node(u,w));
}
for(int i=0; i<m; i++) {
st = new StringTokenizer(br.readLine());
int to = Integer.parseInt(st.nextToken());
int from = Integer.parseInt(st.nextToken());
dfs(to, -1, from, 0);
System.out.println(answer);
}
}
static void dfs(int to, int pa, int from, int cost) {
if(to == from) {
answer = cost;
}
for(Node nxt : list[to]) {
if(nxt.to != pa) {
dfs(nxt.to, to, from, cost+nxt.cost);
}
}
}
}
LCA 풀이
백준 1761번 정점들의 거리는 이와 같은 문제인데, 노드 최대 크기는 4만개, 쿼리는 최대 1만개로 위의 알고리즘으로 해결할 수 없다. 그래서 LCA알고리즘을 통해 풀면 다음과 같다.
import java.io.*;
import java.util.ArrayList;
import java.util.List;
import java.util.StringTokenizer;
public class Main {
static class Node{
int to;
int w;
public Node(int to, int w) {
this.to = to;
this.w = w;
}
}
static int n,h;
static List<Node>[] list;
static int[][] dp;
static int[] dis;
static int[] depth;
static StringBuilder sb = new StringBuilder();
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 m = Integer.parseInt(st.nextToken());
list = new ArrayList[n+1];
for(int i=0; i<n+1; i++) {
list[i] = new ArrayList<>();
}
for(int i=0; i<n-1; i++) {
st = new StringTokenizer(br.readLine());
int from = Integer.parseInt(st.nextToken());
int to = Integer.parseInt(st.nextToken());
int w= Integer.parseInt(st.nextToken());
list[from].add(new Node(to,w));
list[to].add(new Node(from,w));
}
h = getTreeH();
depth = new int[n+1];
dis = new int[n+1];
dp = new int[n+1][h];
init(1,1,0);
fillParents();
for(int i=0; i<m; i++) {
st = new StringTokenizer(br.readLine());
int a = Integer.parseInt(st.nextToken());
int b = Integer.parseInt(st.nextToken());
int res = LCA(a,b);
sb.append(dis[a] + dis[b] -2*dis[res]).append("\n");
}
System.out.println(sb.toString());
}
static int getTreeH() {
return (int)Math.ceil(Math.log(n)/Math.log(2))+1;
}
static void init(int cur, int h, int pa) {
depth[cur] = h;
for(Node nxt : list[cur]) {
if(nxt.to!=pa) {
dis[nxt.to] = dis[cur] + nxt.w;
init(nxt.to, h+1, cur);
dp[nxt.to][0] = cur;
}
}
}
static void fillParents() {
for(int i=1; i<h; i++) {
for(int j=1; j<n+1; j++) {
dp[j][i] = dp[dp[j][i-1]][i-1];
}
}
}
static int LCA(int a, int b) {
int ah = depth[a];
int bh = depth[b];
if(ah < bh) {
int tmp =a;
a= b;
b= tmp;
}
for(int i=h-1; i>=0; i--) {
if(Math.pow(2, i) <= depth[a] - depth[b]) {
a = dp[a][i];
}
}
if(a==b) return a;
for(int i=h-1; i>=0; i--) {
if(dp[a][i] != dp[b][i]) {
a = dp[a][i];
b = dp[b][i];
}
}
return dp[a][0];
}
}
LCA알고리즘을 사용한 코드(위), DFS을 사용한 코드(아래)의 채점 결과는 다음과 같다.
'Dot Algo∙ DS > PS' 카테고리의 다른 글
[BOJ] 백준 1289번 트리의 가중치 (Java) (0) | 2021.10.22 |
---|---|
[BOJ] 백준 19535번 ㄷㄷㄷㅈ (Java) (0) | 2021.10.21 |
[BOJ] 백준 13511번 트리와 쿼리2 (Java) (0) | 2021.10.19 |
[BOJ] 백준 14267번 회사 문화 1 (Java) (0) | 2021.10.18 |
[BOJ] 백준 2251번 물통 (Java) (0) | 2021.10.17 |