ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [백준 1799] - [백트래킹] - 비숍 (JAVA)
    알고리즘/백트래킹 2018. 12. 26. 17:13

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


    이 문제는 이전에 풀었던 N-Queen 문제의 확장판 문제이다.


    2018/11/21 - [알고리즘/백트래킹] - [백준-9663]-[백트래킹] - N-Queen



    그리고 백트래킹 문제의 최고존엄 문제 급이지 않을까 생각한다.


    이 문제를 잘못 접근하게 되면 문제는 풀겠지만 시간 초과에 걸리게 된다. 


    잘못접근하는 대부분의 이슈는 하나의 체스판을 전체 탐색을 할경우 생기게 된다. 


    비숍은 대각선으로 움직인다. 그렇다면 대각선에는 비숍이 위치할 수 없다.


    아래처럼 2,2 에 비숍이 위치할 경우 대각선에 위치한 모든 좌표에는 비숍을 놓을 수 없다.


     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     



    또 여기서 하나 중요하게 고려해야할게 있는데 2, 2에 비숍을 놓았을경우 비숍의 좌표 기준 상, 하, 좌, 우 는 비숍을 놓을 수 있는지 탐색할 필요가 없다.


    즉 시간초과가 나는 가장 큰 이유는 (y, x) 좌표에 비숍을 놓을 경우 상,하,좌,우 까지 같이 탐색을 하기 때문이다.


    체스판은 흑색 칸과 백색 칸이 있고 이 칸들은 한칸 건너서 위치한다.


    흑색과 백색 칸은 서로에게 영향을 주지 않는다.


     

     

     

     

     

     


     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     


    즉 탐색 자체를 흑색칸에 비숍을 놓았을 경우 흑색 칸에 대한 탐색만 하고 백색칸에 비숍을 놓았을 경우 백색칸에 대한 탐색을 진행한 다음 두번의 탐색에 걸쳐 나온 비숍을 놓을 수 있는 자리수를 더해서 출력하면 되는 문제이다. 



    흑색칸은 (1, 1) 부터 탐색을 진행했고


    백색칸은 (1,2) 부터 탐색을 진행했다.


    x의 좌표를 2씩 증가 시키면서 같은 색의 칸을 탐색하게 했고 x 의 좌표가 체스의 크기보다 켜질 경우 

    y 좌표를 1씩 증가 시키고 x 좌표는 y 좌표에 따라서 초기화 해줬다. 


    해당 좌표에 비숍을 놓을 수 있는지에 대한 탐색 자체도 상하좌우에 대각선까지 탐색하지 않고 비숍 기준 대각선으로만 탐색을 진해하여 시간 초과를 방지했다.


    소스

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    import java.io.BufferedReader;
    import java.io.IOException;
    import java.io.InputStreamReader;
    import java.util.StringTokenizer;
     
    public class Main {
        public static int size;
        public static int[][] chess;
        
        public static int[] dy = {-1-111}; // 좌상, 우상, 좌하, 우하
        public static int[] dx = {-11-11}; // 좌상, 우상, 좌하, 우하
        
        public static int b_cnt = 0;
        public static int w_cnt = 0;
        
        public static void main(String[] args) throws IOException{
            BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
            StringTokenizer st = null;
            
            size = Integer.parseInt(br.readLine());
            
            chess = new int[size+1][size+1];
            
            // 입력
            for(int i=1; i <= size; i++) {
                st = new StringTokenizer(br.readLine());
                
                for(int j=1; j <= size; j++) {
                    chess[i][j] = Integer.parseInt(st.nextToken());
                }
            }
            
            // 흑색 자리에 둘 수 있는 비숍의 수를 탐색한다.
            boolean[][] black_visited = new boolean[size+1][size+1];
            black_search(black_visited, 110);
            
            // 백색 자리에 둘 수 있는 비숍의 수를 탐색한다.
            boolean[][] white_visited = new boolean[size+1][size+1];
            white_search(white_visited, 120);
            
            // 흑색, 백색 자리에 둘 수 있는 비숍의 수를 합하여 출력한다.
            System.out.println(b_cnt+w_cnt);
        }
     
        public static void black_search(boolean[][] visited, int y, int x, int cnt) {
            // 검은자리에 둘 수 있는 횟수가 기존 횟수보다 크면 값을 변경한다.
            b_cnt = Math.max(cnt, b_cnt);
            
            // x 표가 체스판의 크기보다 크면 좌표를 다음칸의 첫번째 x 좌표로 이동 시켜준다.
            // 단, 흑색 자리는 y가 홀수일 경우 x의 시작 점이 1부터 시작되고
            //    짝수일 경우 2부터 시작된다.
            if(x > size) {
                y += 1;
                x = (y%2 == 0)?2:1;
            }
            
            // y의 좌표가 체스판의 사이즈를 초과할경우 탐색을 종료한다.
            if(y > size) return;
            
            
            // 현재자리에 비숍을 놓을 수 있는지 확인
            // 현재자리에 비숍을 놓을 수 있다면 비숍을 놓고 다음 탐색을 진행한다.
            // 다음 탐색을 진행할 때에는 x좌표를 2증가 시켜줘야 한다. 이유는 옆 자리는 색이 다른 자리니깐
            // 비숍을 둘 경우에는 cnt를 1 증가 시켜줘야한다.
            if(isAble(visited, y, x)) {
                visited[y][x] = true;
                black_search(visited, y, x+2, cnt+1);
                visited[y][x] = false;
            }
            
            // 현재자리에 비숍을 놓을수 있더라도 안놓을수도 있기 때문에 비숍을 놓지 않았을 경우에 대한 탐색도 진행한다.
            // 다음 탐색을 진행할 때에는 x좌표를 2증가 시켜줘야 한다. 이유는 옆 자리는 색이 다른 자리니깐
            // 비숍을 놓지 않았을 경우에는 cnt를 증가시킬 필요가 없다.
            black_search(visited, y, x+2, cnt);
        }
        
        public static void white_search(boolean[][] visited, int y, int x, int cnt) {
            // 백색 자리에 둘 수 있는 횟수가 기존 횟수보다 크면 값을 변경한다.
            w_cnt = Math.max(cnt, w_cnt);
            
            // x 표가 체스판의 크기보다 크면 좌표를 다음칸의 첫번째 x 좌표로 이동 시켜준다.
            // 단, 백색 자리는 y가 홀수일 경우 x의 시작 점이 2부터 시작되고
            //    짝수일 경우 1부터 시작된다.
            if(x > size) {
                y += 1;
                x = (y%2 == 0)?1:2;
            }
            
            // y의 좌표가 체스판의 사이즈를 초과할경우 탐색을 종료한다.
            if(y > size) return;
            
            // 현재자리에 비숍을 놓을 수 있는지 확인
            // 현재자리에 비숍을 놓을 수 있다면 비숍을 놓고 다음 탐색을 진행한다.
            // 다음 탐색을 진행할 때에는 x좌표를 2증가 시켜줘야 한다. 이유는 옆 자리는 색이 다른 자리니깐
            // 비숍을 둘 경우에는 cnt를 1 증가 시켜줘야한다.
            if(isAble(visited, y, x)) {
                visited[y][x] = true;
                white_search(visited, y, x+2, cnt+1);
                visited[y][x] = false;
            }
            
            // 현재자리에 비숍을 놓을수 있더라도 안놓을수도 있기 때문에 비숍을 놓지 않았을 경우에 대한 탐색도 진행한다.
            // 다음 탐색을 진행할 때에는 x좌표를 2증가 시켜줘야 한다. 이유는 옆 자리는 색이 다른 자리니깐
            // 비숍을 놓지 않았을 경우에는 cnt를 증가시킬 필요가 없다.
            white_search(visited, y, x+2, cnt);
        }
        
        
        public static boolean isAble(boolean[][] visited, int y, int x) {
            if(chess[y][x] == 0return false// 0이면 놓을 수 없다.
            
            for(int i=0; i < 4; i ++) {
                int yy = dy[i] + y;
                int xx = dx[i] + x;
                
                for(int j=1; j <= size; j++) {
                    if(yy > 0 && xx > 0 && yy <= size && xx <= size) {
                        if(visited[yy][xx]) return false;
                        
                        yy += dy[i];
                        xx += dx[i];
                    }
                }
            }
            return true;
        }
    }
    cs


    댓글

Designed by Tistory.