알고리즘/백트레킹
[Algorithm]백트래킹 - 2. 연습(N-퀸 문제)
jhooooon
2025. 5. 8. 21:40
N-퀸 문제
- N-퀸 문제는 NxN의 크기 체스판에 서로 공격하지 못하게 퀸을 최대한 많이 놓을 수 있는 자리를 탐색하는 문제 이다.
- 퀸의 경로는 직선과 대각선으로 움직일 수 있다.
- 첫번째 row[0], column[0] 에 퀸을 두면 row[0]은 더이상 퀸을 두지 못하고 row[1]에 퀸을 두어야 한다.
- row[1]에서도 row[1]의 column[0] 과 column[1]은 각각 직선방향 대각선 방향으로 퀸이 공격할 수 있기 때문에 다른퀸을 두지 못하고 column[2], column[3]에 퀸을 둘수 있다. column[3]에 퀸을 먼저 두어도 되지만 순서상 column[2]에 먼저 퀸을 두고 다음 row로 넘어 간다. 이후 문제가 발생할 시에 백트래킹으로 돌아와 column[3]에 퀸을 두면 된다.
- 두번째퀸을 row[1]의 column[2]에 두게 되면 row[2]에 세번째 세번째 퀸을 둘 수 없다. 원리상 4x4이상의 체스판에서는 각 row 마다 퀸 하나씩을 둘 수있는데 row[2]에 퀸을 둘 수 없다면 이전에 놓여있던 퀸의 자리가 잘못된 것이다. 여기서 백트래킹이 발생한다.
- 백트래킹 해서 row[1]의 퀸 자리를 column[2]에서 column[3]으로 이동 후 다시 탐색을 진행한다.
- 백트래킹 해서 row[2]의 column[1] 자리에 퀸을 두게 되면 row[3]에 퀸을 둘 수 없으므로 또다시 백트래킹이 발생한다.
- 백트래킹 해서 row[2]로 돌아와도 더이상 둘곳이 없으므로 또 다시 백트래킹이 발생해 row[1]로 돌아간다.
- row[1]도 더이상 둘곳이 없으므로 백트래킹 되어 row[0]으로 돌아가 row[0]의 column[0]에 있는 퀸을 row[0]의 column[1]로 퀸을 옮긴다.
- 이런식으로 계속 반복하다 보면 아래 그림 처럼 4x4 체스판에 각 row 마다 하나씩 퀸을 둘 수 있게 되고 각 퀸들은 다른 퀸들을 공격할 수 없다.
소스(Java)
import java.util.*;
class Solution {
int[] pieces;
int n = 0; // 체스판 크기
public void solution(int n) {
this.n = n;
this.pieces = new int[n]; // 체스말의 위치
Arrays.fill(this.pieces, -1); // 체스말의 위치를 -1로 초기화
play(0); // row 0부터 시작
System.out.println(Arrays.toString(this.pieces));
}
public boolean play(int row) {
if(this.n == row) { // 모든 row가 다 채워 졌으면
return true; // true 리턴
}
for(int c = 0; c < this.n; c++) { // 해당 row의 모든 column을 조회
if(canPlay(row, c)) { // 체스말을 둘수있는 자리인지 확인
pieces[row] = c; // 체스말을 둘수 있으면 체스말의 자리 저장
if (play(row+1)) { // 다음 row의 말의 자리 찾으러 GO
return true; // 모든 row가 다 채워 졌으면 더 진행하지 않고 return
}
// 모든 row가 채워지지 않았다면 체스말의 자리 초기화 후 다음 column 조회
pieces[row] = -1;
}
}
// for문이 다 돌때까지 말 자리를 못찾았다면 이전 row로 돌아가 말 위치 재선정(백트래킹)
return false;
}
public boolean canPlay(int row, int column) {
// 0~ 현재 row 까지 반복 pieceRow는 이미 저장 되어있는 말의 row
for(int pieceRow = 0; pieceRow < row; pieceRow++) {
int pieceColumn = this.pieces[pieceRow]; // 저장 되어있는 말의 Column
if(pieceColumn == column) { // 저장 되어있는 말의 Column과 현재 Column의 값이 같으면
return false; // 말을 놓지 못함.
}
// (현재 컬럼) - (저장된 말의 컬럼)의 절대값과 (현재 로우) - (저장된 말의 로우)의 절대값이 같으면 대각선임.
if(Math.abs(column - pieceColumn) == Math.abs(pieceRow - row)) {
return false; // 대각선에는 말을 놓지 못함.
}
}
return true; // 저장된 모든 말과 같은 컬럼도 아니고 대각선에 위치 하지 않았으면 말을 놓을 수 있음.
}
public static void main(String[] args) {
Solution solution = new Solution();
solution.solution(4);
}
}