프로그래머스 <안전지대> 자바 Lv.0

bada·2024년 4월 17일

Algorithm

목록 보기
2/4
post-thumbnail

코딩테스트 연습 > 코딩테스트 입문 > 안전지대

https://school.programmers.co.kr/learn/courses/30/lessons/120866

난 참 예외처리에 약하다.(경험부족..) 이론을 계속봐도 이론과 실습의 괴리가 너무 큰 파트같다. 이번 문제에서 인덱스예외처리를 해결하는 아이디어 조차도 나는 잘 떠올리지 못했다.

IndexOutOfBoundsException 에러를 잡지 못해서 결국 해결책을 컨닝했다.
오류와 해결방식은 간단했다.

나의 오류

지뢰가 위치한 셀이 보드의 가장자리일 경우는 주변셀이 이미 없으므로 위험지역으로 처리하기 위한 코드에서 각각 +1, -1을 해버릴때 인덱스의 범위를 벗어나는 오류였다.

해결1번

위험지역 가능성이 있는 셀들을 모두 if문으로 범위를 체크한다.

class Solution {
    public int solution(int[][] board) {
        int answer = 0;
        String[][] arr = new String[board.length][board.length];
        //새 2차배열 arr에 지뢰좌표와 일치한 인덱스의 요소에 x를 대입한다.
        for(int i = 0; i < board.length; i++) {
            for(int j = 0; j < board.length; j++) {
                if(board[i][j] == 1) {
                    arr[i][j] = "x";
                } else arr[i][j] = "o";
            }
        }
        //요소값이 x인 arr배열의 인덱스를 반환하여 board배열에 대입, 위험지역을 1로 바꿔준다.
        for(int i = 0; i < arr.length; i++) {
            for(int j = 0; j < arr.length; j++) {
                if(arr[i][j].equals("x")) {
                    //가장자리에 위치한 지뢰의 경우, IndexOutOfBoundsException 예외처리 필요.
                    if(i > 0) board[i-1][j] = 1;
                    if(i < arr.length-1) board[i+1][j] = 1;
                    if(j > 0) board[i][j-1] = 1;
                    if(j < arr.length-1) board[i][j+1] = 1;
                    if(i > 0 && j > 0) board[i-1][j-1] = 1;
                    if(i < arr.length-1 && j > 0) board[i+1][j-1] = 1;
                    if(i > 0 && j < arr.length-1) board[i-1][j+1] = 1;
                    if(i < arr.length-1 && j < arr.length-1) board[i+1][j+1] = 1;
                }
            }
        }
        //최종적으로 안전지역(0인 지역)의 수를 셈한다.
        for(int i = 0; i < board.length; i++) {
            for(int j = 0; j < board.length; j++) {
                if(!(board[i][j] == 1)) answer++;
            }
        }
        return answer;
    }
}

처음 풀이에서는 보드와 같은 크기의 new보드를 생성했다. 이유는 지뢰가 연속으로 인접해있을 때에는 위험지역으로 분류할때 이미 지뢰인 셀의 흔적을 지워버리기 때문이다. 그래서 new보드를 생성해서 지뢰인 셀을 모두 체크를 하고 new보드의 지뢰유무를 체크하여 기존보드와 동일한 좌표값을 가져와 위험지역을 분류했다.

개선할 점이 바로 보였다.

1. 객체지향적 접근! 위험지역을 확인하는 세부동작은 메서드화 하자.
2. 8줄의 if문 향연..을 심플하게 줄여보자.

해결2번

class Solution {
    public int solution(int[][] board) {
        int answer = 0;
        
        //지뢰가 있는 셀 찾기
        for(int i = 0; i < board.length; i++) {
            for(int j = 0; j < board.length; j++) {
                if(board[i][j] == 1) {
                    //위험지역으로 분류하기
                    changeDangerCell(board, i, j);
                }
            }
        }
        
        //안전지역인 셀 수 반환하기
        for(int i = 0; i < board.length; i++) {
            for(int j = 0; j < board.length; j++) {
                if(board[i][j] == 0) answer++;
            }
        }
        
        return answer;
    }
    
    //지뢰에 인접한 8개의 셀을 위험지역으로 분류하는 메서드
    public void changeDangerCell(int[][] board, int i, int j) {
        int length = board.length;
        //인접한 8개의 셀의 좌표값을 가진 배열
        int[][] directions = {{0,-1},{0,1},{-1,-1},{-1,0},{-1,1},{1,-1},{1,0},{1,1}};
        
        for(int[] dir : directions) {
            int x = i + dir[0];
            int y = j + dir[1];
            //보드의 가장자리이거나 이미 지뢰가 매설된 지역이 아니면 위험지역(-1)로 분류하기
            if(x >= 0 && y >= 0 && x < length && y < length && board[x][y] == 0) {
                board[x][y] = -1;
            }
        }
    }
}

new보드배열을 만들지 않아도 인접한 지역의 지뢰의 유무를 확인할 수 있었다.

board[x][y] == 0

처럼 조건을 걸어서 지뢰가 아닌 셀만 위험지역으로 바꾸면 된다.. 초간단. 🤦‍♀️
그리고, if문으로 일일이 나열했던 코드는 위험지역 가능성이 있는 좌표값을 배열에 담아 반복문으로 돌리는 동작으로 수정할 수 있다.

profile
하루 세번 목 당기기

0개의 댓글