[JAVA] 1861: 정사각형 방

SWEA-정사각형 방 문제

티어 및 알고리즘 분류

티어 – D4

알고리즘 분류

  • DFS 구현
  • 완전 탐색

문제

N 개의 방이 N×N형태로 늘어서 있다.

위에서 i번째 줄의 왼쪽에서 j번째 방에는 1이상 N² 이하의 수 A_i,j가 적혀 있으며, 이 숫자는 모든 방에 대해 서로 다르다.

당신이 어떤 방에 있다면, 상하좌우에 있는 다른 방으로 이동할 수 있다.

물론 이동하려는 방이 존재해야 하고, 이동하려는 방에 적힌 숫자가 현재 방에 적힌 숫자보다 정확히 1 더 커야 한다.

처음 어떤 수가 적힌 방에서 있어야 가장 많은 개수의 방을 이동할 수 있는지 구하는 프로그램을 작성하라.

[입력]

첫 번째 줄에 테스트 케이스의 수 T가 주어진다.

각 테스트 케이스의 첫 번째 줄에는 하나의 정수 N (1 ≤ N ≤ 10³)가 주어진다.

다음 N개의 줄에는 i번째 줄에는 N개의 정수 A_i,1, … , A_i,N (1 ≤ A_i,j ≤ N²)이 공백 하나로 구분되어 주어진다.

A_i,j는 모두 서로 다른 수이다.

[출력]

각 테스트 케이스마다 ‘#x’(x는 테스트케이스 번호를 의미하며 1부터 시작한다)를 출력하고, 한 칸을 띄운 후, 처음에 출발해야 하는 방 번호와 최대 몇 개의 방을 이동할 수 있는지를 공백으로 구분하여 출력한다.

이동할 수 있는 방의 개수가 최대인 방이 여럿이라면 그 중에서 적힌 수가 가장 작은 것을 출력한다.


문제 접근 방식

1. 입력 및 초기화

입력으로 테스트 케이스 수와 N×N 크기의 방 정보를 받는다.

각 방에 적힌 숫자를 2차원 배열 map에 저장한다.

각 테스트 케이스마다 DFS 탐색을 위한 방문 배열 v를 초기화한다.

2. DFS 탐색 수행

모든 방을 시작점으로 DFS를 수행한다.

각 시작점에서 상하좌우로 이동 가능한지 검사한다.

이동 가능한 조건은 인접한 방의 숫자가 현재 방의 숫자보다 정확히 1 큰 경우이다.

방문 여부를 체크하며 경로의 길이를 누적한다.

3. 최대 이동 경로 갱신

DFS 수행 중 이동 횟수 cnt가 기존의 최대 이동 횟수 max보다 크거나 같으면 갱신한다.

동일한 이동 횟수일 경우, 시작점의 숫자가 더 작은 경우로 결과를 업데이트한다.

4. 결과 출력

모든 DFS 탐색 후, 최대 이동 가능 경로의 시작 방 번호와 이동 횟수를 출력한다.


구현 코드

import java.util.Arrays;
import java.util.Scanner;

class SSol1861{
    static int value = 0;
    static int max = Integer.MIN_VALUE;

    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int testCase = sc.nextInt();
        for(int t = 1; t <= testCase; t++){
            int n = sc.nextInt();
            int[][] map = new int[n][n];

            for(int i = 0; i < n; i++){
                for(int j = 0; j< n; j++){
                    map[i][j] = sc.nextInt();
                }
            }

            for(int i = 0; i < n; i++){
                for(int j = 0; j < n; j++){
                    int[] sp = new int[]{i,j};
                    boolean[][] v = new boolean[n][n];
                    v[sp[0]][sp[1]] = true;

                    dfs(map, 1, sp, Arrays.copyOf(sp, sp.length), n, v);

                }
            }

            System.out.println("#"+t+" "+value + " " + max);
            value = 0;
            max = Integer.MIN_VALUE;
        }
    }

    static void dfs(int[][] map, int cnt, int[] sp, int[] mp, int n, boolean[][] v){
        int[] dx = {0,0,1,-1};
        int[] dy = {1,-1,0,0};

        for(int i = 0; i < 4; i++){
            int xdx = mp[0] + dx[i];
            int ydy = mp[1] + dy[i];
            if(xdx >= 0 && xdx < n && ydy >= 0 && ydy < n && !v[xdx][ydy]){
                if(Math.abs(map[xdx][ydy] - map[mp[0]][mp[1]]) == 1){
                    mp[0] = xdx;
                    mp[1] = ydy;
                    v[xdx][ydy] = true;
                    cnt++;

                    if(max <= cnt){
                        if(max == cnt){
                            if(value > map[sp[0]][sp[1]]){
                                value = map[sp[0]][sp[1]];
                                max = cnt;
                            }
                        }else{
                            value = map[sp[0]][sp[1]];
                            max = cnt;
                        }

                    }

                    dfs(map, cnt, sp, mp, n, v);
                }
            }
        }
    }

}

마치며

문제의 조건을 만족하기 위해 DFS 탐색을 활용한 완전 탐색 방식을 선택하였다.

불필요한 중복 탐색을 피하기 위해 방문 배열을 사용하였다.

DFS를 통해 모든 경우의 수를 탐색하며 최적의 결과를 도출할 수 있었다.

문제 풀이 과정에서 깊이 우선 탐색의 중요성을 다시 한 번 깨달을 수 있었다.

다른 문제에서도 DFS를 활용할 수 있음을 확인할 수 있었다.

위로 스크롤