ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Q. 1981 자바 : 배열에서 이동
    코딩테스트/백준_Java 2025. 5. 25. 23:48

    Q. 1981 자바 : 배열에서 이동

     

    문제 : boj.kr/1981

    P5 난이도의 문제이다.

     

    문제를 간단히 설명하자면

    그래프가 주어지고 1,1 ~ n,n까지 이동하려고 할 때,

    거쳐간 수들 중 최댓값과 최솟값의 차이가 가장 작아지는 경우를 구하는 문제이다.


    처음에 이분 탐색과 BFS를 활용한 방식으로 풀었고 아래와 같이 풀었지만 틀렸다.

    1. 거쳐간 수들의 (최댓값-최솟값) = mid로 정의한다.

    2. BFS 방식으로 이동하며 mid가 가능한지 조회한다.

    3. 조건에 따라 이분탐색 방식으로 mid의 범위를 좁혀나간다

    이 방법은 잘못된 방식이었다.

    static boolean determination(int mid) {
        visit = new boolean[n][n];
        Queue<int[]> que = new ArrayDeque<>();
        que.add(new int[] {0, 0, map[0][0], map[0][0]});
    
        while (!que.isEmpty()) {
            int[] cur = que.poll();
            int curY = cur[0];
            int curX = cur[1];
            int curMin = cur[2];
            int curMax = cur[3];
    
            for (int i = 0; i < 4; i++) {
                int nextY = curY + dy[i];
                int nextX = curX + dx[i];
    
                if (nextY < 0 || nextX < 0 || nextY >= n || nextX >= n) continue;
                if (visit[nextY][nextX]) continue;
    
                int nextMin = Math.min(curMin, map[nextY][nextX]);
                int nextMax = Math.max(curMax, map[nextY][nextX]);
                if (nextMax - nextMin > mid) continue;
    
                visit[nextY][nextX] = true;
                que.add(new int[] {nextY, nextX, nextMin, nextMax});
            }
        }
    
        return visit[n - 1][n - 1];
    }

    단순 BFS 방식으로는 경로 중 최대값과 최소값을 계속 갱신해야 하는데,
    동일한 위치라도 도달 경로가 다르면 값 범위가 달라질 수 있기 때문에 visit[][] 배열만으로 중복 방문을 확인하기가 어려웠다.

     

    그렇다고 가능한 모든 경우의 수를 따지기 위해 DFS 방식을 사용한다면 O(2^100)으로 시간 초과가 예상되었고

    아래와 같은 방식으로 풀었다.


    위 조건에서 하나만 바꾸면 된다.

    1. 거쳐간 수들 중 (최댓값-최솟값) = mid로 정의한다.

    2. BFS 방식으로 이동하며 거쳐간 수가 [최소값, 최소값 + mid] 구간 내에 존재하는지 확인한다.

    [최소값, 최소값 + mid] 구간 내에 거쳐간 수가 존재한다면  거쳐 간 수의(최댓값 - 최솟값) <= mid 임이 보장된다.

    3. 조건에 따라 이분탐색 방식으로 mid의 범위를 좁혀나간다

     

    n <= 100이고 최대 구간값 x는 200이다.

    1, 3번 과정은 이분탐색으로 O(log x)

    2번 과정은 구간 계산은 O(x), BFS는 O(n^2)이므로 시간복잡도는 O(x * n^2 log x)이다.

    코드는 아래와 같다.

    import java.io.*;
    import java.util.*;
    
    class Main {
    
        static BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        static StringBuffer sb = new StringBuffer();
        static StringTokenizer st;
    
        static int n;
        static int[][] map;
    
        static int[] dy = {0, 1, 0, -1};
        static int[] dx = {1, 0, -1, 0};
        static boolean[][] visit;
    
    
        static void input() throws IOException{
            n = Integer.parseInt(br.readLine());
    
            map = new int[n][n];
            for (int i = 0; i < n; i++) {
                st = new StringTokenizer(br.readLine());
                for (int j = 0; j < n; j++) {
                    map[i][j] = Integer.parseInt(st.nextToken());
                }
            }
        }
    
        static void pro() {
    
            int left = 0, right = 200, result = 0;
            while (left <= right) {
                int mid = (left + right) / 2;
                if (determination(mid)) {
                    result = mid;
                    right = mid - 1;
                } else {
                    left = mid + 1;
                }
            }
    
            System.out.println(result);
        }
    
        static boolean determination(int mid) {
            for (int min = 0; min + mid <= 200; min++) {
                int max = min + mid;
                if (map[0][0] < min || map[0][0] > max) continue;
    
                visit = new boolean[n][n];
                Queue<int[]> que = new ArrayDeque<>();
                que.add(new int[] {0, 0});
                visit[0][0] = true;
    
                while (!que.isEmpty()) {
                    int[] cur = que.poll();
                    int curY = cur[0];
                    int curX = cur[1];
    
                    if (curY == n - 1 && curX == n - 1) return true;
    
                    for (int d = 0; d < 4; d++) {
                        int ny = curY + dy[d];
                        int nx = curX + dx[d];
    
                        if (ny < 0 || nx < 0 || ny >= n || nx >= n) continue;
                        if (visit[ny][nx]) continue;
                        if (map[ny][nx] < min || map[ny][nx] > max) continue;
    
                        visit[ny][nx] = true;
                        que.add(new int[] {ny, nx});
                    }
                }
            }
    
            return false;
        }
    
        public static void main(String[] args) throws Exception{
            input();
            pro();
        }
    }

     

    반응형

    댓글

Designed by Tistory.