Algorithm

[백준 Gold 5] 1987 알파벳 - Java

Ray 2021. 4. 14. 17:16

문제 링크 : www.acmicpc.net/problem/1987

 

1987번: 알파벳

세로 R칸, 가로 C칸으로 된 표 모양의 보드가 있다. 보드의 각 칸에는 대문자 알파벳이 하나씩 적혀 있고, 좌측 상단 칸 (1행 1열) 에는 말이 놓여 있다. 말은 상하좌우로 인접한 네 칸 중의 한 칸으

www.acmicpc.net

 

접근 과정

  • 시작점은 좌측 상단으로 고정,  이동방향은 상-하-좌-우.
  • 이동방향이 상화좌우이기에, DP가 아닌 DFS로 접근
  • 알파벳이 중복되지 않고 갈 수 있는 경로를 탐색
  • 어떻게 지나온 길의 알파벳을 저장하고 비교할건지가 관건 (저장된 알파벳의 수 = 이동 칸의 수)
  • HashSet -> boolean[] -> BitMask로 방식을 바꿔가며 시간을 줄여나감. 

 

소스 코드 및 결과

   Code 1 : HashSet<Character>

import java.io.*;
import java.util.*;

/* 
    알파벳 
    dfs (HashSet)
*/
public class BOJ1987 {

    static int max, R, C;
    static HashSet<Character> alphabet; // 사용한 알파벳을 저장
    static char[][] board; 
    static int[][] direction = {{-1,0}, {1,0},{0,-1},{0,1}}; // 상하좌우 이동

    
    public static void main(String[] args) throws IOException {
        
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        StringTokenizer st = new StringTokenizer(br.readLine());

        R = Integer.parseInt(st.nextToken());
        C = Integer.parseInt(st.nextToken());

        board = new char[R][C];
        for(int i= 0; i<R; i++){
            board[i] = br.readLine().toCharArray();
        }

        max = 0;  // 최대 이동 칸의 수
        alphabet = new HashSet<>();
        alphabet.add(board[0][0]); // 시작 칸의 알파벳을 저장
        dfs(0, 0);

        System.out.println(max);
    }

    static void dfs(int r, int c){

        boolean possible = false;  // 다음 이동이 가능한지 체크

        for(int i = 0; i<4; i++){
            int nr = r+direction[i][0];
            int nc = c+direction[i][1];

            if (nr>=0 && nr<R && nc>=0 && nc<C) {  // 다음 이동 좌표가 볌위안에 존재하는지 
                if (alphabet.contains(board[nr][nc])) { // 다음 좌표의 알파벳을 이미 지나왔는지
                    continue;
                }
                possible = true; // 다음 이동 가능

                alphabet.add(board[nr][nc]); // 알파벳을 추가하고 DFS
                dfs(nr, nc);
                alphabet.remove(board[nr][nc]); // 추가한 알파벳 제거
            }
        }

        if (!possible) {  // 더 이상 이동이 불가한 경우, 이동 거리를 갱신
            max = Math.max(max, alphabet.size());
            return;
        }
    }
}

   결과:

   

   Code 2 : boolean[26]

import java.io.*;
import java.util.*;

/* 
    알파벳 
    dfs (boolean[])
*/
public class BOJ1987 {

    static int max, R, C;
    static char[][] board;
    static boolean[] visit;  // 사용한 알파벳을 boolean값으로 저장
    static int[][] direction = { { -1, 0 }, { 1, 0 }, { 0, -1 }, { 0, 1 } };  // 상하좌우 이동

    public static void main(String[] args) throws IOException {
        
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        StringTokenizer st = new StringTokenizer(br.readLine());

        R = Integer.parseInt(st.nextToken());
        C = Integer.parseInt(st.nextToken());

        board = new char[R][C];
        for(int i= 0; i<R; i++){
            board[i] = br.readLine().toCharArray();
        }

        visit = new boolean[26];  // 알파벳의 수만큼 할당
        visit[board[0][0]-'A'] = true;  // 시작 칸의 알파벳을 체크

        max = 0;
       
        dfs(0, 0, 1); // 줄, 칸, 이동거리

        System.out.println(max);
    }

    static void dfs(int r, int c, int d){

        for(int i = 0; i<4; i++){
            int nr = r + direction[i][0];
            int nc = c + direction[i][1];

            if (nr>=0 && nr<R && nc>=0 && nc<C) {  // 다음 이동 칸이 범위 안에 있다면
                if (!visit[board[nr][nc]-'A']) { // 이미 사용된 알파벳이 아니라면
                    visit[board[nr][nc]-'A'] = true;
                    dfs(nr, nc, d+1); // 거리를 추가하고 dfs 진행
                    visit[board[nr][nc]-'A'] = false;
                }                
            }
        }
        max = Math.max(max, d); // 이동거리 갱신
    }
}

   결과:

   

   Code 3 : BitMast

import java.io.*;
import java.util.*;

/* 
    알파벳 
    dfs (비트마스크)
*/
public class BOJ1987 {

    static int max, R, C;
    static char[][] board;
    static int[][] visit; // 해당 칸에 도착할 때까지 사용한 알파벳을 비트(int)값으로 저장
    static int[][] direction = { { -1, 0 }, { 1, 0 }, { 0, -1 }, { 0, 1 } }; // 상하좌우 이동

    public static void main(String[] args) throws IOException {
        
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        StringTokenizer st = new StringTokenizer(br.readLine());

        R = Integer.parseInt(st.nextToken());
        C = Integer.parseInt(st.nextToken());

        board = new char[R][C];
        for(int i= 0; i<R; i++){
            board[i] = br.readLine().toCharArray();
        }

        visit = new int[R][C];

        max = 0;
       
        dfs(0, 0,1<<board[0][0]-'A' ,1);  // 줄, 칸, 시작 칸의 알파벳을 비트값으로 저장하고 시작, 이동거리 

        System.out.println(max);
    }

    static void dfs(int r, int c, int check_bit, int d){

        if (visit[r][c] == check_bit) { // 조회하려는 비트값(이동경로)를 이미 조회했다면.
            return;
        }        

        visit[r][c] = check_bit; // 해당 칸의 이동경로(비트값) 갱신

        max = Math.max(max, d);  // 현재까지의 최대 이동거리 갱신

        for(int i = 0; i<4; i++){
            int nr = r + direction[i][0];
            int nc = c + direction[i][1];

            if (nr >= 0 && nr<R && nc>=0 && nc<C) { // 다음 이동 칸이 볌위내에 존재한다면
                if ((check_bit & 1<<board[nr][nc]-'A') == 0) { // 이미 사용된 알파벳이 아니라면
                    dfs(nr, nc,check_bit|1<<board[nr][nc]-'A', d+1); // 알파벳 비트를 추가하고 dfs 지속
                }
            }
        }        
    }
}

   결과:

 

 

P.S 비트마스크는 아직 익숙하지 않아 좀 더 학습이 필요함.