View

[백준] 9663 - N-Queen

sm_amoled 2021. 8. 12. 21:17
300x250

문제링크

https://www.acmicpc.net/problem/9663

문제

N-Queen 문제는 크기가 N × N인 체스판 위에 퀸 N개를 서로 공격할 수 없게 놓는 문제이다.

N이 주어졌을 때, 퀸을 놓는 방법의 수를 구하는 프로그램을 작성하시오.

입력

첫째 줄에 N이 주어진다. (1 ≤ N < 15)

출력

첫째 줄에 퀸 N개를 서로 공격할 수 없게 놓는 경우의 수를 출력한다.

조건

  • 시간 제한 : 10s
  • 메모리 제한 : 128MB

해설

가로, 세로, 대각선으로 공격이 가능한 퀸을 NxN 체스판 위에 N개 배치하였을때, 서로 공격하지 않는 경우의 수를 구하는 문제이다. 한 행 / 한 열에는 하나의 퀸만이 들어갈 수 있다는 점을 이용하면 좀 더 쉽게 해결이 가능하다.

각 행에 퀸이 몇 번째 열에 들어가있는지를 저장하는 배열/벡터를 이용해 재귀적으로 퀸의 배치 경우의 수를 헤아리면 문제를 해결할 수 있다.

풀이

크기가 N인 벡터 하나를 선언해주었다. 이 벡터에는 0번 부터 시작하는 보드의 각 행에 몇번째 열에 퀸이 들어있는지를 나타낸다.

int N;
cin >> N;

vector<int> board(N);
Q _ _ _       → board [ 1  3  _  _ ]
_ _ Q _        (현재 4X4 보드에 2개의 Queen을 담은 경우)
_ _ _ _
_ _ _ _

문제 해결을 위해 queen 함수를 재귀적으로 선언해주었다. queen 함수이 인자인 board는 위에서 설명한 queen의 각 행에서의 열 정보를 담는 벡터이고, row는 그 중에서 몇 번째 행을 계산중인지를 나타낸다. 해당 row의 모든 column에 대해서 검사를 하는데, 만약 해당 row 전에 배치해둔 queen이 공격을 할 수 있는 자리인 경우에는 survive를 false로 바꾼다. 어떤 queen도 공격을 할 수 없으면 그대로 survive는 true로 남아있다. 먼저 배치한 queen의 공격 유효 검사를 마친 뒤에 survive가 아직 true라면 (공격받지 않는 자리라면) 해당 row의 i번째 column에 퀸을 놓고(board[row]=i) row+1에 대한 처리를 호출한다.(queen(board, row+1) 만약 공격받는 자리여서 survive가 false라면 그 다음 column에 queen을 배치하는 검사를 수행하게된다.

void queen(vector<int> board, int row) {
    int maxBoard = board.size();
    
    for(int i = 0; i < maxBoard; i++) {
        bool survive = true;
        
        for(int j = 1; j <= row; j++) {
            if(board[row-j] == i || board[row-j] == i-j || board[row-j] == i+j) {
                survive = false;
                break;
            }
        }
        
        if(survive) {
            board[row] = i;
            queen(board, row+1);
        }
    }
}

만약에 row가 N과 같아진다면 (board는 N-1번째 행까지밖에 없다!) 모든 퀸이 살아남아 호출된 함수이므로 전역변수로 선언된 count에 1을 더해주고 함수를 종료한다.

if(maxBoard == row) {count++; return;}


코멘트

모든 경우의 수에 대해 검사를 하다보니, 생각보다 코드 실행 속도가 엄청엄청 느려진다. 문제 통과 속도가 5000ms 정도가 나왔다. 그래도 아래에 고민보다는 나은 수치! ㅋㅋㅋ


코드

#include <iostream>
#include <vector>

using namespace std;

int count = 0;

void queen(vector<int> board, int row) {
    int maxBoard = board.size();
    
    if(maxBoard == row) {count++; return;}
    
    for(int i = 0; i < maxBoard; i++) {
        bool survive = true;
        
        for(int j = 1; j <= row; j++) {
            if(board[row-j] == i || board[row-j] == i-j || board[row-j] == i+j) {
                survive = false;
                break;
            }
        }
        
        if(survive) {
            board[row] = i;
            queen(board, row+1);
        }
    }
    
}

int main() {
    int N;
    cin >> N;
    
    vector<int> board(N);
    
    queen(board, 0);
    
    cout << count;
    
	return 0;
}

문제 해결에 대한 고민

처음에는 2차원 배열을 선언해 board 자체를 만들려고 했었다. 어차피 한 행에 하나의 퀸 밖에 안들어가기 때문에, 불필요한 메모리가 많이 들었고, 무엇보다도 2차원 벡터의 각 원소에 접근하는데 반복해서 소요되는 시간이 커져서 시간 초과가 발생했다. 이를 해결하기 위해 위 풀이처럼 1차원 벡터로 바꾸어줬다.

#include <iostream>
#include <vector>

using namespace std;

int count = 0;

void queen(vector<vector<bool>> board, int N, int row) {
    int temp = 0;
    
    if(row == N) {
        count++;
        return;
    }
    
    for(int column = 0; column < N; column++) {
        int temp = 0;
        bool survive = true;
        while(row - ++temp >= 0) {
            // 세로줄
            if(board[row-temp][column]) {
                survive = false;
                break;
            }
            // 왼쪽 대각선
            else if (column-temp >= 0 && board[row-temp][column-temp]) {
                survive = false;
                break;
            }
            // 오른쪽 대각선
            else if (column+temp < N && board[row-temp][column+temp]) {
                survive = false;
                break;
            }
        }
        
        if(survive) {
            board[row][column] = true;
            queen(board, N, row+1);
            board[row][column] = false;
        }
    }
}

int main() {
    int N;
    cin >> N;
    
    
    vector<vector<bool>> board(N, vector<bool>(N, false));
    
    queen(board, N, 0);
    
    cout << count;
    
	return 0;
}
320x100

'Develop > 알고리즘' 카테고리의 다른 글

[백준] 15652 - N과 M (4)  (0) 2021.08.12
[백준] -7568 덩치  (0) 2021.08.12
[백준] 1427 - 소트인사이드  (0) 2021.08.12
[백준] 1018 - 체스판 다시 칠하기  (0) 2021.08.12
[백준] 15650 - N과 M (2)  (0) 2021.08.12
Share Link
reply
반응형
«   2024/05   »
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