본문 바로가기
PS/BOJ

[자바] 백준 11812 - K진 트리 (java)

by Nahwasa 2023. 8. 3.

문제 : boj11812

 

 

필요 알고리즘

  • 최소 공통 조상 (LCA)
    • LCA 문제이다. 근데 몰라도 푸는데 딱히 지장은 없다.

※ 제 코드에서 왜 main 함수에 로직을 직접 작성하지 않았는지, 왜 Scanner를 쓰지 않고 BufferedReader를 사용했는지 등에 대해서는 '자바로 백준 풀 때의 팁 및 주의점' 글을 참고해주세요. 백준을 자바로 풀어보려고 시작하시는 분이나, 백준에서 자바로 풀 때의 팁을 원하시는 분들도 보시는걸 추천드립니다.

 

 

풀이

  LCA를 안다면 그걸로 바로 접근 가능하고, 몰라도 딱히 상관은 없다. 어차피 첫 깊이엔 1개, 두번째 깊이엔 K개, 세번째 깊이엔 K^2개, ... 의 노드가 존재할꺼다. 그러므로 x와 y를 입력받을 시, K로 나누다보면 점점 부모 노드의 번호를 알 수 있고 결국 루트까지 도달 가능하다.

 

  LCA를 쓴다면 x와 y 중 더 깊이가 높은 쪽을 작은쪽까지 맞춘 후, 두개를 동시에 진행하게 된다. 내 경우엔 굳이 쓰고싶지 않아서 둘 중 깊이가 더 낮은쪽을 그냥 루트까지 진행하면서 만나는 부모노드들의 번호를 모두 찾아둔다. 그 후 더 높은 쪽을 마찬가지로 루트까지 진행하다가, 이미 찾아둔 부모노드들 중 하나를 만난다면 최소 공통 조상을 찾은셈이니 거리를 더해서 출력해주면 된다.

private long getDist(final long n, final int k, final long x, final long y) {
    long hi = x>y?x:y;	// 깊이가 더 높은쪽
    long lo = x>y?y:x;	// 깊이가 더 낮은쪽

    if (k == 1) return hi-lo;

    Map<Long, Integer> exist = new HashMap<>();
    int dist = 0;
    exist.put(lo, dist);	// 우선 깊이가 낮은쪽을 자기자신부터 시작해
    while (lo != 0) {	// 루트까지 부모들을 찾으면서 거리를 재둔다.
        if (lo%k==0) lo = lo/k-1;
        else lo/=k;

        exist.put(lo, ++dist);
    }

    dist = 0;
    while (hi != 0) {	// 이후 깊이가 높은쪽을 진행하다가 위에서 찾아둔 것 중 하나가 나오면
        ++dist;
        if (hi%k==0) hi = hi/k-1;
        else hi/=k;
        if (exist.containsKey(hi))
            return dist+exist.get(hi);	// 그게 최소 공통 조상을 찾은거니 두 거리를 더하면 된다.
    }

    return -1;	// 어차피 루트까지 올라가면 만나므로 여기까지 진행될 일은 없긴하다.
}

 

 

코드 : github

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.HashMap;
import java.util.Map;
import java.util.StringTokenizer;

public class Main {

    static BufferedReader br = new BufferedReader(new InputStreamReader(System.in), 1<<16);

    public static void main(String[] args) throws Exception {
        new Main().solution();
    }

    private void solution() throws Exception {
        StringTokenizer st = new StringTokenizer(br.readLine());
        long n = Long.parseLong(st.nextToken());
        int k = Integer.parseInt(st.nextToken());
        int q = Integer.parseInt(st.nextToken());

        StringBuilder sb = new StringBuilder();
        while (q-->0) {
            st = new StringTokenizer(br.readLine());
            long x = Long.parseLong(st.nextToken()) - 1;
            long y = Long.parseLong(st.nextToken()) - 1;

            sb.append(getDist(n, k, x, y)).append('\n');
        }

        System.out.print(sb);
    }

    private long getDist(final long n, final int k, final long x, final long y) {
        long hi = x>y?x:y;
        long lo = x>y?y:x;

        if (k == 1) return hi-lo;

        Map<Long, Integer> exist = new HashMap<>();
        int dist = 0;
        exist.put(lo, dist);
        while (lo != 0) {
            if (lo%k==0) lo = lo/k-1;
            else lo/=k;

            exist.put(lo, ++dist);
        }

        dist = 0;
        while (hi != 0) {
            ++dist;
            if (hi%k==0) hi = hi/k-1;
            else hi/=k;
            if (exist.containsKey(hi))
                return dist+exist.get(hi);
        }

        return -1;
    }
}