[백준 1799] 비숍

최민길(Gale)·2023년 7월 3일
1

알고리즘

목록 보기
82/172

문제 링크 : https://www.acmicpc.net/problem/1799

이 문제는 백트래킹을 이용하여 풀 수 있습니다. 처음 이 문제를 봤을 때 비숍의 특징을 이용해서 왼쪽 아래 방향으로 이동할 경우 y+x 값이 같은 칸은 비숍을 둘 수 없고 오른쪽 아래 방향으로 이동할 경우 Math.abs(y-x)값이 같은 칸은 비숍을 둘 수 없다는 규칙을 이용해서 매 백트래킹마다 위 반복문을 통해 check 배열을 true 및 false로 세팅하였습니다. 하지만 이 경우 시간 초과가 발생해 다른 해결책을 찾아보아야 합니다.

이 문제는 모든 체스판을 탐색하지 않도록 진행하는 것이 중요합니다. 이를 위해 검정색 이동과 하얀색 이동을 나누어 고려합니다. 즉 기존의 경우 시간 복잡도가 O(N^N)에서 O(2 * (N/2)^(N/2) )로 감소시킬 수 있습니다. 각 칸에서 비숍을 놓을 수 있는지를 체크한 후 존재한다면 값을 1만큼 증가시켜 재귀호출합니다. 이 때 주의할 점은 놓을 수 없어도 비숍을 놓지 않고 넘기는 경우도 존재하므로 현재 개수를 증가시키지 않고 재귀호출하는 부분도 필요합니다. 이 때 검정색과 하얀색은 교차하기 때문에 시작값의 x값을 조정해줍니다. 예를 들어 검정색이 0,0일 경우 그 아랫줄의 검정색은 1,1 , 그 아랫줄의 검정색은 2,0 이 되기 때문에 x값은 시작값에 따라 0 또는 1로 고정됩니다. 이를 파악하여 값을 증가시키고 y가 넘어간다면 함수를 종료한 후 결과값의 합을 출력합니다.

다음은 코드입니다.

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

class Main{
    static int N,blackRes = 0, whiteRes = 0;
    static int[] dy = {-1,-1,1,1}, dx = {1,-1,1,-1};
    static boolean[][] canDo,blackCheck,whiteCheck;

    public static void main(String[] args) throws Exception{
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        N = Integer.parseInt(br.readLine());
        canDo = new boolean[N][N];

        for(int i=0;i<N;i++){
            StringTokenizer st = new StringTokenizer(br.readLine());
            for(int j=0;j<N;j++){
                int val = Integer.parseInt(st.nextToken());
                canDo[i][j] = val == 1;
            }
        }

        blackCheck = new boolean[N][N];
        blackBacktracking(blackCheck,0,0,0);

        whiteCheck = new boolean[N][N];
        whiteBacktracking(blackCheck,0,1,0);

        System.out.println(blackRes+whiteRes);
    }

    static void blackBacktracking(boolean[][] check, int y, int x, int cnt){
        blackRes = Math.max(blackRes,cnt);

        if(x>=N){
            y++;
            x = (y%2==0) ? 0:1;
        }

        if(y>=N) return;

        if(canPut(check,y,x)){
            check[y][x] = true;
            blackBacktracking(check,y,x+2,cnt+1);
            check[y][x] = false;
        }

        blackBacktracking(check,y,x+2,cnt);
    }

    static void whiteBacktracking(boolean[][] check, int y, int x, int cnt){
        whiteRes = Math.max(whiteRes,cnt);

        if(x>=N){
            y++;
            x = (y%2==0) ? 1:0;
        }

        if(y>=N) return;

        if(canPut(check,y,x)){
            check[y][x] = true;
            whiteBacktracking(check,y,x+2,cnt+1);
            check[y][x] = false;
        }

        whiteBacktracking(check,y,x+2,cnt);
    }

    static boolean canPut(boolean[][] check, int y, int x){
        if(!canDo[y][x]) return false;

        for(int i=0;i<4;i++){
            int ny = y + dy[i];
            int nx = x + dx[i];

            for(int j=0;j<N;j++){
                if(ny>=0 && ny<N && nx>=0 && nx<N) {
                    if(check[ny][nx]) return false;

                    ny += dy[i];
                    nx += dx[i];
                }
            }
        }
        return true;
    }
}

참고 자료
https://pangsblog.tistory.com/84

profile
저는 상황에 맞는 최적의 솔루션을 깊고 정확한 개념의 이해를 통한 다양한 방식으로 해결해오면서 지난 3년 동안 신규 서비스를 20만 회원 서비스로 성장시킨 Software Developer 최민길입니다.

0개의 댓글