본문 바로가기
PS/BOJ

[자바] 백준 21278 - 호석이 두 마리 치킨 (java)

by Nahwasa 2023. 6. 13.

목차

    문제 : boj21278

     

     

    필요 알고리즘

    • 플로이드 와샬, BFS, 다익스트라 등 최단 거리 알고리즘 아무거나 
      • 최단 거리를 구할 수 있는 알고리즘으로 풀 수 있는 문제이다.

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

     

     

    풀이

    생각 과정은 이하와 같다.

     

    1. 모든 건물에 대해 모든 쌍에 대한 최단 거리를 알 수 있다고 해보자.

     

    2. 그렇다면 N개 중 2개의 건물을 고르기로 확정하고, '1'에서 구한 거리 정보로 '최단 시간의 총합'을 구할 수 있을 것이다.

     

    3. 그럼 '1'을 유효한 시간 내에 구할 수 있을까? -> N이 100밖에 안되므로 일단 처음 생각난건 대충 플로이드 와샬 박으면 될 것 같았다. 그래도 O(N^3) 밖에 안된다! 그리고 플로이드 와샬로 되면 BFS나 다익스트라로는 당연히 더 널널하게 된다.

     

    4. '2'는 유효한 시간 내에 가능할까? 2개의 건물 확정이 O(N^2)이고, 각각의 경우에 대해 모든 건물까지의 최단 시간의 총합을 구해야 하므로 O(N^3)으로 가능하다.

     

    5. 최종적으로 O(N^3 + N^3) = O(N^3)으로 가능하다! 그럼 구현 하자.

     

    ---

     

    참고로 일반적인 플로이드 와샬 코드와 다르게 일정 범위에 대해서만 진행된 부분이 헷갈릴 수 있을 것 같다.

    for (int k = 0; k < n; k++) {
        for (int i = 0; i < n; i++) {
            for (int j = i+1; j < n; j++) {
                map[i][j] = map[j][i] = min(map[i][j], map[i][k] + map[k][j]);
            }
        }
    }

    어차피 양방향 간선이므로, A에서 B로 가는 최단 거리가 확정되면 B에서 A로 가는 최단 거리도 동일하게 확정되는 셈이다. 그래서 모든 NxN 쌍을 볼 필요 없이 위처럼 보면 된다.

     

    ---

     

    '만약 이러한 건물 조합이 여러 개라면, 건물 번호 중 작은 게 더 작을수록, 작은 번호가 같다면 큰 번호가 더 작을수록 좋은 건물 조합이다.'

     

    위의 조건이 까다로워 보일 수도 있는데, 사실 그냥 작은 번호부터 차례대로 2쌍식 골라서 보면 자동으로 해결된다.

    for (int i = 0; i < n; i++) {
        for (int j = i+1; j < n; j++) {
    
            ...
    
            if (min > sum) {	// 이게 만족되면 위 조건도 항상 만족된다.
                min = sum;
                ansA = i;
                ansB = j;
            }
    
        }
    }

     

     

    코드 : github

    import java.io.BufferedReader;
    import java.io.IOException;
    import java.io.InputStreamReader;
    import java.util.Arrays;
    import java.util.StringTokenizer;
    
    import static java.lang.Math.*;
    
    public class Main {
        static BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        private static final int INF = 100007;
    
        public static void main(String[] args) throws Exception {
            new Main().solution();
        }
    
        private void solution() throws Exception {
            StringTokenizer st = new StringTokenizer(br.readLine());
            int n = Integer.parseInt(st.nextToken());
            int m = Integer.parseInt(st.nextToken());
    
            int[][] map = initMap(n, m);
    
            floydWarshall(n, map);
    
            System.out.println(answer(n, map));
        }
    
        private int[][] initMap(final int n, int m) throws IOException {
            int[][] map = new int[n][n];
            for (int[] row : map)
                Arrays.fill(row, INF);
    
            while (m-->0) {
                StringTokenizer st = new StringTokenizer(br.readLine());
                int a = Integer.parseInt(st.nextToken()) - 1;
                int b = Integer.parseInt(st.nextToken()) - 1;
    
                map[a][b] = map[b][a] = 1;
            }
    
            return map;
        }
    
        private void floydWarshall(final int n, int[][] map) {
            for (int k = 0; k < n; k++) {
                for (int i = 0; i < n; i++) {
                    for (int j = i+1; j < n; j++) {
                        map[i][j] = map[j][i] = min(map[i][j], map[i][k] + map[k][j]);
                    }
                }
            }
        }
    
        private String answer(final int n, final int[][] map) {
            int min = INF;
            int ansA = 0;
            int ansB = 0;
    
            for (int i = 0; i < n; i++) {
                for (int j = i+1; j < n; j++) {
    
                    int sum = 0;
                    for (int k = 0; k < n; k++) {
                        if (i == k || j == k) continue;
    
                        sum += min(map[i][k], map[j][k]);
                    }
    
                    if (min > sum) {
                        min = sum;
                        ansA = i;
                        ansB = j;
                    }
    
                }
            }
    
            return String.format("%d %d %d", ansA+1, ansB+1, min*2);
        }
    }

     

    댓글