C05 The_ten_queens
08 The ten queens
참고 : https://chanhuiseok.github.io/posts/baek-1/
youtube : n-queens problem
0. 조건...?
보통은 N * N 체스판 위에, (4 <= N <= 15)
퀸 N개를 서로 공격할 수 없도록 놓는 경우의 수를 구하는 문제입니다.
++ 42Seoul에서는,
1. 퀸의 위치를 문자열로 나타내고,
2. 모든 방법의 총 개수를 return해야 합니다.
퀸은 같은 행, 같은 열, 대각선 /, \ 방향 모두 놓을 수 없다는 특징이 있습니다.
-> 행 마다 퀸을 하나씩 놓아 줌으로써, 특징 중 하나인 행을 고려하지 않도록 합니다.
그러면, 대각선은 어떻게 판단하죠...?
이 대각선은, x와 y를 같이 증가시키거나 감소시킨다면 찾을 수 있습니다!
\
\
\
\
이 대각선은 x를 감소시키고 y값을 증가시키거나,
x를 증가시키고 y값을 감소시키면 찾을 수 있습니다!!
/
/
/
/
이 때, 두 조건에는 공통점이 있어요 : )
4 * 4 맵에 만약, (2, 1)에 퀸이 놓여있다고 합시다...
그렇다면 (3, 2), (1, 0),
(1, 2), (0, 3), (3, 0) 이 대각선에 해당하는 것을 알 수 있는데,
|3 - 2| = |2 - 1|
|1 - 2| = |1 - 0|
|1 - 2| = |2 - 1|
등등등...
이 값들의 절대값, |행 번호 차이| = |열 번호 차이| 가 같다는 것을 알 수 있습니다
행 번호 차이 == 열 번호 차이 ==> 같은 대각선상!!
(초등학교, 중학교 때 배운 직선의 방정식 y = x, y = -x를 생각해봅시다.)
그리고, 문제에서 1차원 문자열로 표현해서 출력해주어야 하기 때문에,
배열을 이용해서 풉니다.
0열 (인덱스) 퀸의 위치(행). 1열 (인덱스) 퀸의 위치(행)
1행 퀸의 위치, 2행 퀸의 위치. 이렇게 이용할 수 있기 때문에, 1차원 배열로도 충분합니다.
(== 배열의 인덱스를 하나의 정보로 사용합니다!!!)
문제에서는 열로 퀸의 위치 (행) 를 표기했으므로
같은 행이 나오면 안되고, 인덱스 - 인덱스 == 행 - 행 인 값이 나오면 안됩니다!!
1142 = X !!! (행이 같은 곳에 넣어졌다!!!)
1423 = X !!! (2번째와 3번째 인덱스 차이 = 1, 2번째와 3번째 행 차이 = 1로 같아졌다!!)
그렇다면, 해당하는 조건은
if (queen[i] != queen[cnt], i - cnt != queen[i] - queen[cnt])
가 되겠다.
조건 판단
int judge(int x, char *board)
{
int i;
int tmp;
i = 0;
while (i < x)
{
tmp = board[x] - board[i];
if (tmp < 0)
tmp *= -1;
if (board[x] == board[i] || x - i == tmp)
return (0);
i++;
}
return (1);
}
int nqueen(int x)
{
int i;
int cnt;
cnt = 0;
i = 0;
if (x == 10)
{
cnt++;
print...
return (cnt);
}
while (i < 10)
{
board[x] = i;
if (judge(x, board))
nqueen(x + 1);
i++;
}
return (cnt);
}
1. 백 트래킹...
백 트래킹은, DFS를 개선한 알고리즘이라고 합니다.
DFS : 깊이 우선 탐색
모든 방법을 탐색하는 완전탐색방법이지만,
백 트래킹은 정답이 될 수 없는 것은 탐색하지 않음으로써, 탐색 효율을 높였습니다!
==> 통상 "가지치기"라고 부릅니다.
그럼 DFS가 뭔가요...?
트리나, 그래프에서 한 루트로 검색합니다. 최대한 깊숙이 들어가서 확인한 후, 없다면 다시 돌아가 다른 루트를 탐색합니다.
[재귀함수] 나 [스택]으로 구현합니다.
일전의 C 문제에서, 재귀함수를 사용해보셨을 것이라고 생각합니다.
재귀함수를 호출하고, 밑에 write, putchar 문을 두어서 최근의 것부터 출력되게 만들었잖아요!!!
비슷하다고 생각하시면 됩니다!!!
단, 여기서는 재귀함수를 실행하는데 조건을 주었습니다!
-
방문할 그래프나, 트리, 배열등의 자료구조가 생겼습니다.
-
먼저, 반복문을 통해, 인덱스를 변화시켰습니다.
-
만약 해당 인덱스에 이미 접근한 상태라면, 다시 들어가지 않습니다.
(+dfs) -
또 다시 조건을 주어, 해당 조건을 만족하지 않는다면 가지치기를 해주어 들어가지 않습니다!
(+백 트래킹)
간단한 소스코드 예제
출처 : 위키백과 (dfs)
참고 : https://youtu.be/7C9RgOcvkvo
const int MAX = 100'001;
bool visited[MAX]; // 방문 배열. visited[node] = true이면 node는 방문이 끝난 상태이다.
void dfs(const vector<int> graph[], int current) { // graph는 인접 리스트, current는 현재 노드
visited[current] = true; // current 방문
for(int next: graph[current]) { // current의 인접 노드를 확인한다. 이 노드를 next라고 하자.
if(!visited[next]) { // 만일 next에 방문하지 않았다면
dfs(graph, next); // next 방문
}
}
}
/* ************************************************************************** */
/* */
/* ::: :::::::: */
/* ft_ten_queens_puzzle.c :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: hyojeong <[email protected]> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2022/02/12 01:13:42 by hyojeong #+# #+# */
/* Updated: 2022/02/14 09:40:48 by hyojeong ### ########.fr */
/* */
/* ************************************************************************** */
#include <unistd.h>
# 먼저, 해당 위치에 퀸을 놓을 수 있을지 판단하는 judge 함수
# ==> 백트래킹에 해당하겠죠!!!
int judge(int x, int board[])
{
int i;
int tmp;
i = 0;
while (i < x)
{
tmp = board[x] - board[i];
if (tmp < 0)
tmp *= -1;
if (board[x] == board[i] || x - i == tmp)
return (0);
i++;
}
return (1);
}
# 단순히 보드를 프린트해주는, 프린트 보드 함수
void print_board(int board[])
{
int index;
char tmp;
index = 0;
while (index < 10)
{
tmp = board[index] + 48;
write(1, &tmp, 1);
index++;
}
write(1, "\n", 1);
}
# 핵심. cnt의 값을 기록해주고, 해당 위치에 퀸을 놓을 수 있다면, 다음 열로 넘어가는, 재귀를 이용한 퀸 함수!
void queen(int x, int *cnt, int board[])
{
int i;
i = 0;
if (x == 10)
{
*cnt = *cnt + 1;
print_board(board);
return ;
}
while (i < 10)
{
board[x] = i;
if (judge(x, board))
queen(x + 1, cnt, board);
i++;
}
}
# cnt를 반환시키는, 문제에서 구현하라는 프로토타입 함수
int ft_ten_queens_puzzle(void)
{
int cnt;
int index;
int board[10];
cnt = 0;
index = 0;
while (index < 10)
board[index++] = 0;
queen(0, &cnt, board);
return (cnt);
}
흐름
-
먼저, board에 0000000000을 채워줍니다.
-
퀸 재귀 함수를 실행합니다.
2-0. 왜?? (0, cnt, board)로 실행시키는 이유는 ??
해당 인덱스에 넣을 퀸을 결정하려고요!!
2-1. 퀸 재귀 함수에, board[0] = 0을 넣고, 판단합니다. 다른 곳에 퀸이 채워져 있지 않아, 당연히 통과하고 넘어갑니다.
2-2. 그리고, board의 1번째 인덱스에 채울 퀸의 위치를 결정하려고 넘어갑니다.
그런데, 해당 재귀함수는 반복문으로 실행되었고, 밑에 i++가 있잖아요??
그렇게 조건이 맞다면, 또 들어가고, 아니라면 나와서 반복문이 다시 실행되고...
이렇게 조건에 맞는 모든 퀸이 출력되고, 개수가 반환됩니다.
Author And Source
이 문제에 관하여(C05 The_ten_queens), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://velog.io/@1984/C05-Thetenqueens저자 귀속: 원작자 정보가 원작자 URL에 포함되어 있으며 저작권은 원작자 소유입니다.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)