N-Queen
문제
N-Queen 문제는 크기가 N × N인 체스판 위에 퀸 N개를 서로 공격할 수 없게 놓는 문제이다.
N이 주어졌을 때, 퀸을 놓는 방법의 수를 구하는 프로그램을 작성하시오.
입력
첫째 줄에 N이 주어진다. (1 ≤ N < 15)
출력
첫째 줄에 퀸 N개를 서로 공격할 수 없게 놓는 경우의 수를 출력한다.
예제 입력 1
8
예제 출력 1
92
백트래킹에 대해 공부하면서 소문이 자자한 N-Queen 문제를 풀어봤다.
백준 골드로 표시된 문제는 처음 풀어봐서 약간 긴장했는데, 아니나 다를까 체스의 ㅊ도 모르는데 무작정 퀸 배치한다고 문제 던져줘서 너무 난감했다..^^
퀸이 어떻게 배치되는지 체스에 대한 룰을 전혀 몰라서 아래 블로그를 통해 퀸이 어떻게 배치될 수 있는지에 대해서만 읽었다!! (혼자 풀어보고 싶어서 스크롤 더 내려보고 싶은 마음을 억제했다)
감사한 블로거님의 사진을 가져와서 보자면, 퀸이 위치한 네 면과 대각선에는 다른 퀸을 배치할 수 없다.
#include <algorithm>
#include <iostream>
#include <vector>
#include <string>
#include <cmath>
using namespace std;
int N;
int arr[16];
int cnt = 0;
void DFS(int depth)
{
if (depth == N)
{
cnt++;
//for (int i = 0; i < N; i++)
// cout << arr[i] << " ";
//cout << endl << "count : " << cnt << endl;
return;
}
for (int i = 0; i < N; i++)
{
bool check = true;
for (int j = 0, k = depth-1; j < depth; j++, k--)
{
// 현재 Depth에서부터 위로 한 칸씩 올라가며 대각선 판별
if (arr[depth - j - 1] == i || abs(arr[depth - j - 1] - i) == abs(depth-k))
{
check = false;
break;
}
}
if (check)
{
arr[depth] = i;
DFS(depth + 1);
}
}
}
int main()
{
ios_base::sync_with_stdio(0);
cin.tie(0);
cin >> N;
DFS(0);
cout << cnt;
return 0;
}
처음 코드를 제출해서 맞은 소스코드는 위와 같다.
백트래킹 문제인 만큼 DFS를 이용하였고, 한 줄 한 줄 퀸이 배치될 수 있는 공간을 1차원 배열인 arr로 선언하였다.
DFS의 매개변수인 depth를 이용해 몇번째 행인지를 판단하고, arr의 인덱스를 이용해 몇번째 열인지를 판단하였다.
해당 문제의 핵심은 퀸의 대각선을 구분하는 방법이었는데, 나는 현재 Depth로부터 한 칸씩 위로 올라가며 이전에 놓여진 퀸들(arr 배열의 원소)의 위치를 보며 비교하였다.
위로 올라갈수록 arr 배열의 인덱스는 depth-j-1 가 되어 같은 열에 있는지 == i 를 통해 비교하고, 대각선에 있는지 k 를 통해 비교했다.
첫번째 for문인 i = 0부터 N까지는 현재 Depth에서 퀸이 어느 열에 놓일 수 있는지를 찾기 위함이고, 두번째 for문인 j = 0부터 depth까지는 이전에 놓인 퀸들의 위치를 비교하며 놓일 수 있는지를 찾기 위함이다.
여기서 나의 실수는 멍청하게 depth 위치에서부터 위로 거슬러 올라가며 arr 배열 요소를 비교하려 했다는 점이다..!!
그냥 첫번째 놓았던 arr[0] 번째 퀸부터 검사했으면 k값도 필요없고, arr[depth-j-i] 같은 복잡한 인덱스도 필요없었는데 당시 문제를 풀 때 생각이 거슬러 올라가야한다고 고정되어 있던 것 같다 ㅎㅎ;;
#include <algorithm>
#include <iostream>
#include <vector>
#include <string>
#include <cmath>
using namespace std;
int N;
int arr[16];
int cnt = 0;
void DFS(int depth)
{
if (depth == N)
{
cnt++;
//for (int i = 0; i < N; i++)
// cout << arr[i] << " ";
//cout << endl << "count : " << cnt << endl;
return;
}
for (int i = 0; i < N; i++)
{
bool check = true;
//for (int j = 0, k = depth-1; j < depth; j++, k--)
for (int j = 0; j < depth; j++)
{
// 현재 Depth에서부터 위로 한 칸씩 올라가며 대각선 판별
//if (arr[depth - j - 1] == i || abs(arr[depth - j - 1] - i) == abs(depth-k))
//{
// check = false;
// break;
//}
// 첫번째 줄부터 한 칸씩 내려가며 대각선 판별
if(arr[j] == i || abs(arr[j] - i) == abs(depth-j))
{
check = false;
break;
}
}
if (check)
{
arr[depth] = i;
DFS(depth + 1);
}
}
}
int main()
{
ios_base::sync_with_stdio(0);
cin.tie(0);
cin >> N;
DFS(0);
cout << cnt;
return 0;
}
문제를 성공하고 나서 다시 0에서부터 아래로 내려가며 같은 열과 대각선을 검사하는 코드를 작성해보았다.
이렇게 풀면 k 값도 필요없고 arr의 인덱스도 j만 들어가면 돼서 코드가 훨씬 간결하고 보기 좋아졌다.
대각선은 비교하고자 하는 퀸의 x, y 값과 현재 depth와 i 값을 x, y로 보고 각각끼리 빼서 절댓값이 같으면 대각선이다.
쉽게말하자면, abs(이전 퀸의 위치.X - 현재 위치.X) == abs(이전 퀸의 위치.Y - 현재 위치.Y) 가 되면 대각선에 위치한다는 뜻이다.
각 행 - 행, 열 - 열 의 절댓값이 같으면 된다는 뜻이다!
문제를 풀어보고 나서 다른분들의 블로그들을 보니 대각선을 구하는 과정에서 많은 어려움을 겪으신 것 같았다.
나는 대각선을 구하는 방법(?)에 대해선 예전에 깨달은 적이 있어서 "절댓값을 이용하면 되겠구나~" 싶었는데, 혼자 거꾸로 비교하며 자체 하드모드를 즐겼다 ㅎㅎ;
[알고리즘] 백트래킹(backtracking) 알아보기(& N-Queen 문제 풀이)
안녕하세요😎 백엔드 개발자 제임스입니다 :) 오늘 알아볼 내용은 백트래킹(Backtracking) 알고리즘입니다. 해당 개념은 전에 다루었던, [동적 계획법과 분할 정복]과 동일하게 문제를 푸는 전략이
kang-james.tistory.com
https://www.acmicpc.net/problem/9663
9663번: N-Queen
N-Queen 문제는 크기가 N × N인 체스판 위에 퀸 N개를 서로 공격할 수 없게 놓는 문제이다. N이 주어졌을 때, 퀸을 놓는 방법의 수를 구하는 프로그램을 작성하시오.
www.acmicpc.net
'💻코딩테스트 > 백준' 카테고리의 다른 글
[백준/C++] 1012번 : 유기농 배추 (DFS/BFS 연습) (0) | 2023.06.28 |
---|---|
[백준/C++] 2606번 : 바이러스 (DFS/BFS 연습) (0) | 2023.06.28 |
[백준/C++] 15649번 : N과 M(1) (백트래킹 연습) (0) | 2023.06.23 |
[백준/C++] 2839번 : 설탕 배달 (Greedy 연습) (0) | 2023.06.21 |
[백준/C++] 1436번 : 영화감독 숌 (브루트 포스) (0) | 2023.06.21 |