읽기 전

  • 해당 게시물은 작성자가 학습한 내용을 정리 및 요약 목적으로 작성되었으므로 오역 및 오개념이 있을 수 있습니다.
  • 바로잡기 위한 의견을 제시해주시면 매우 감사하겠습니다.

N Queen Problem

N Queen Problem 이란 N*N 체스판이 주어졌을 때 N개의 queen을 서로 공격할 수 없는 위치에 배치할 수 있는가에 대한 문제이다.

Backtracking_004_01

위 그림처럼 4*4 체스판에 queen을 배치하면 어떠한 여왕도 서로의 공격 경로에 위치하지 않음을 알 수 있다, 그리고 이 배치를 배열로 표현하면 여왕이 있는 곳을 '1', 없는 곳을 '0'으로 표현할 수 있다.

{ 0, 1, 0, 0}
{ 0, 0, 0, 1}
{ 1, 0, 0, 0}
{ 0, 0, 1, 0}

Naive Algorithm

보드에 queen을 배치할 수 있는 경우를 모두 도출한 뒤 제약조건을 만족하는 경우에 출력한다.

시도하지 않은 경우의 수가 있다면
{
    새로운 경우의 수를 생성하고
    만약 queen들이 해당 경우의 수에서 공격할 수 없다면
    {
        경우의 수 출력
    }
}

Backtracking Algorithm

먼저 좌측 첫 번째 column부터 queen들을 배치한다 그리고 queen을 배치할 때 이미 배치된 queen들과 충돌이 발생하는 지 검사한다. 현재 column에서 전투가 발생하지 않는 row가 존재한다면 해당 row 지점에 queen을 배치한다. 만약 충돌이 발생하지 않는 row를 찾을 수 없다면 backtrack 후 false를 return한다.

  1. 가장 좌측의 column에서 시작한다

  2. 만약 모든 queen들의 배치가 완료되면 true를 return한다.

  3. 현재 column에서의 모든 row에 대해 다음을 시도한다.

    a) 만약 queen이 [row, column]에 성공적으로 위치할 수 있으면 체크 후 다음 column으로 넘어가서 재귀탐색한다.

    b) 만약 [row, column]의 배치가 마지막 queen의 배치가 되면 true를 return하여 탐색을 종료한다.

    c) 만약 배치했음에도 이후 단계에서 어떠한 해답이 존재하지 않는다면 backtrack 후 a)로 돌아가 다음 row에 대해 시도한다.

  4. 만약 모든 row에 대해 시도했음에도 해당사항이 없으면 false를 return하여 이전 단계에서 backtrack하도록 한다.

C코드

#include <stdio.h>
#define N 4
void printSolution(int board[N][N])
{
for (int i = 0; i < N; i++)
{
for (int j = 0; j < N; j++)
{
printf(" %d", board[i][j]);
}
printf("\n");
}
}
bool isSafe(int board[N][N], int row, int col)
{
int i, j;
for (i = 0; i < col; i++) // 해당 row에 대해
{
if (board[row][i] == 1) // 다른 col에 queen이 있다면
{
return false; // false 반환
}
}
for (i = row, j = col; i >= 0 && j >= 0; i--, j--)
{
if (board[i][j] == 1)
{
return false; // 좌측 상단 대각선에 queen 존재 검사
}
}
for (i = row, j = col; i < N && j >= 0; i++, j--)
{
if (board[i][j] == 1)
{
return false; // 좌측 하단 대각선에 queen 존재 검사
}
}
return true;
}
bool sovleNQUtil(int board[N][N], int col)
{
if (col >= N)
{
return true; // 끝까지 배치했으면 탐색 종료
}
for (int i = 0; i < N; i++)
{
if (isSafe(board, i, col)) // 유효한 좌표라면
{
board[i][col] = 1; // 해당 좌표에 queen 배치
if (sovleNQUtil(board, col + 1)) // 다음 col 탐색
{
return true; // 문제 없으면 true 반환
}
else
{
board[i][col] = 0; // backtracking
}
}
}
return false; // 이전 단계로 반환하여 backtrack trigger 작동
}
bool solveNQ()
{
int board[N][N] = { { 0, 0, 0, 0 },
{ 0, 0, 0, 0 },
{ 0, 0, 0, 0 },
{ 0, 0, 0, 0 } };
if (sovleNQUtil(board, 0) == false)
{
printf("There's no Solution!");
return false;
}
else
{
printSolution(board);
return true;
}
}
int main()
{
solveNQ();
return 0;
}

Python 코드

def printSolution(board):
for i in range(N):
for j in range(N):
print(board[i][j], end = " ") # 한칸씩 띄워서 출력
print() # 개행
def isSafe(board, row, col):
for i in range(col):
if board[row][i] == 1:
return False # 같은 row에 queen이 있는지 검사
for i, j in zip(range(row, -1, -1), range(col, -1, -1)):
if board[i][j] == 1:
return False # 좌측 상단 대각선에 queen이 있는지 검사
for i, j in zip(range(row, N), range(col, -1, -1)):
if board[i][j] == 1:
return False # 좌측 하단 대각선에 queen이 있는지 검사
return True
def solveNQUtil(board, col):
if col >= N:
return True # col이 끝까지 도달하면 탐색 종료
for i in range(N):
if isSafe(board, i, col): # 유효한 위치라면
board[i][col] = 1 # 해당 좌표에 queen 배치
if solveNQUtil(board, col + 1) == 1: # 다음 col 탐색 진행
return True # 이후에 문제가 없으면 이전 단계에 통보
else:
board[i][col] = 0 # 문제가 발생한다면 backtrack
return False # 이전 단계에 통보하여 backtrack trigger 작동
def solveNQ():
board = [ [0, 0, 0, 0],
[0, 0, 0, 0],
[0, 0, 0, 0],
[0, 0, 0, 0] ]
if solveNQUtil(board, 0) == False:
print("There's no Solution")
else:
printSolution(board)
global N
N = 4
solveNQ()

C++ 코드

c++ 코드는 C 코드와 문법을 비교했을 때 출력문을 제외하고는 없어서 주석을 생략하였다.

#include <bits/stdc++.h>
using namespace std;
#define N 4
void printSolution(int board[N][N])
{
for (int i = 0; i < N; i++)
{
for (int j = 0; j < N; j++)
{
cout << board[i][j] << " ";
}
cout << endl;
}
}
bool isSafe(int board[N][N], int row, int col)
{
int i, j;
for (i = col; i >= 0; i--)
{
if (board[row][i] == 1)
{
return false;
}
}
for (i = row, j = col; i >= 0 && j >= 0; i--, j--)
{
if (board[i][j] == 1)
{
return false;
}
}
for (i = row, j = col; i < N && j >= 0; i++, j--)
{
if (board[i][j] == 1)
{
return false;
}
}
return true;
}
bool solveNQUtil(int board[N][N], int col)
{
if (col >= N)
{
return true;
}
for (int i = 0; i < N; i++)
{
if (isSafe(board, i, col) == true)
{
board[i][col] = 1;
if (solveNQUtil(board, col + 1) == true)
{
return true;
}
else
{
board[i][col] = 0;
}
}
}
return false;
}
bool solveNQ()
{
int board[N][N] = { { 0, 0, 0, 0 },
{ 0, 0, 0, 0 },
{ 0, 0, 0, 0 },
{ 0, 0, 0, 0 } };
if (solveNQUtil(board, 0) == true)
{
printSolution(board);
return true;
}
else
{
cout << "There's no Solution!" << endl;
return false;
}
}
int main()
{
solveNQ();
return 0;
}

참고자료

  1. N Queen Problem | Backtracking-3

+ Recent posts