궤도

[백준] 9663번 : N-Queen 본문

💻 현생/⛓ 알고리즘

[백준] 9663번 : N-Queen

영이오 2021. 3. 21. 15:23

문제

 


풀이

 

N-Queen 문제는 백트래킹의 대표적인 문제이다. 근데 난 얘가 너무 어렵다...

이런 체스판에 퀸을 놓고 이 퀸들이 서로 공격할 수 없게 놓는 문제이다.

퀸은 가로세로대각선으로 이동할 수 있으니 어떠한 퀸들도 같은 행열대각선에 위치하지 않도록 배치해야 한다.


소스코드

 

#include <iostream>

using namespace std;
const int SIZE = 15;

int n, ans;
int check[SIZE];

bool promising(int num) {
    int idx = 0;
    while (idx < num) { //이미 놓여있는 모든 퀸에 대해
        if (check[num] == check[idx] || abs(check[num] - check[idx]) == (num - idx)) //같은 행, 같은 대각선 체크
            return false;
        idx++;
    }
    return true;
}

void backtracking(int cnt) {
    if (cnt == n) { //기저 조건
        ans++;
        return;
    }
    for (int i = 0; i < n; i++) {
        check[cnt] = i; //i행 cnt열에 퀸 배치
        if (promising(cnt))
            backtracking(cnt + 1);
    }
}

/**
 * 4368ms
 */
int main() {
    //입력
    cin >> n;

    //연산
    backtracking(0);

    //출력
    cout << ans << '\n';
}

사실 이 문제를 푼지 오래돼서 기억이 가물가물하지만 설명을 해보겠다.

 

int n, ans;
int check[SIZE];

tot_cnt는 전체 경우의 수를 담을 int 변수이다.

처음엔 체스판 자체를 구현하고자 해서 visited를 2차원 bool 배열로 했더니 시간초과가 됐다.

그래서 잘 생각해보니 굳이 2차원 배열을 구현할 필요가 없었다.

 

어쩌다보니 아이콘이 폰이지만 아무튼...

두개 이상의 퀸이 같은 열에 있는 것은 불가능하다. 그렇기 때문에 visited 배열을 int로 선언하고, 그 값에 퀸의 행을 저장하면 1차원 배열로도 행과 열을 모두 표현할 수 있다.

예를 들어 visited[1] = 2라면 퀸 하나가 1열 2행에 있다는 뜻이 되는 것이다.

 

void backtracking(int cnt) {
    if (cnt == n) { //기저 조건
        ans++;
        return;
    }
    for (int i = 0; i < n; i++) {
        check[cnt] = i; //i행 cnt열에 퀸 배치
        if (promising(cnt))
            backtracking(cnt + 1);
    }
}

그리고 일단 promising 함수 전에 queens 함수부터 보자.

종료 조건은 굳이 설명이 필요없을 것 같고, 행을 하나씩 옆으로 이동하며(cnt +1) 특정 열(i)에 퀸을 배치해본다.

이제 내가 방금 배치한 이 퀸이 이전까지 배치된 퀸을 공격하지 않는지 확인하기 위해 promising 함수를 사용한다.

만약 적절한 위치라면 이를 적용하고 다음 행에 퀸을 배치하기 위해 queens(cnt + 1)을 호출한다.

그렇다면 promising 함수에선 어떻게 이 퀸의 위치가 적절한지 확인할까?

 

bool promising(int num) {
    int idx = 0;
    while (idx < num) { //이미 놓여있는 모든 퀸에 대해
        if (check[num] == check[idx] || abs(check[num] - check[idx]) == (num - idx)) //같은 행, 같은 대각선 체크
            return false;
        idx++;
    }
    return true;
}

index가 나타내는 즉 visited[index]는 이번에 새로 배치한 퀸이 된다. 1행부터 index행 직전까지 while문을 돌며 새로운 퀸이 이전의 퀸들을 공격할 가능성이 있는지 살펴본다.

visited[index] == visited[k]라면 두 퀸의 열이 동일하다는 뜻이고, abs(visited[index] - visited[k]) == (index - k) 그러니까 두 퀸의 행과 열의 차이가 동일하다는 것은 두 퀸이 같은 대각선에 위치한다는 뜻이다.

이렇게 모든 퀸을 확인하는 동안 문제가 없었다면 true를 반환한다.

 

붉은색 퀸(그림에선 폰)을 이번에 새로 배치한 visited[index]라고 하면

while문을 돌며 푸른색 퀸(visited[k])와 위치관계를 확인해보는 것이다.

Comments