이전에 풀었던 암호 만들기 문제를 풀고 난 후 백트래킹 개념에 대해서 더 자세히 알아보고 이해하기 위해 백트래킹으로 유명한 문제인 해당 문제를 풀어보았다.
당연하게도 백트래킹 기법을 이용해서 문제를 해결한다.
0. 첫번째 행부터 시작한다.
1. 첫번째 열부터 퀸을 놓으면서 진행한다.
2. 퀸을 놓으면 해당 체스판의 상태가 유망한지(promising) 판단한다.
3. 만약 유망하다면 다음 행으로 이동해서 퀸을 놓기 시작한다.
4. 만약 유망하지 않다면 더 이상 퀸을 놓지 않고 돌아간다.
유망하지 않을 경우 퀸을 더 이상 놓지 않고 돌아가는 이러한 방법을 백트래킹이라고 한다.
DFS(또는 BFS)으로 모든 Node를 검색한 뒤의 유망성을 점검하여 유망하지 않으면 돌아간 후 다른 분기(branch)를 탐색하는 방식이다.
더 자세한 내용은 백트래킹에 대해 설명한 문서를 확인할 수 있다.
def promising(x):
for i in range(x):
if row[x] == row[i] or abs(row[x] - row[i]) == abs(x - i):
return False
return True
def nqueen(x):
global ans
if x == n:
ans += 1
return
for i in range(n):
if visited[i]:
continue
row[x] = i # [x, i]에 퀸을 위치
if promising(x):
visited[i] = True # i열 퀸 배치 표시
nqueen(x + 1) # i열에 퀸을 배치시킨 후 다음 열로 이동
visited[i] = False # i열 퀸 배치 해제
결국 BackTracking 기법은 DFS, BFS와 같이 모든 경우의 수를 방문하는 탐색 방법에서 중간에 해당 조건을 비교할 수만 있다면 조건을 비교하면서 탐색을 진행하여 유망성이 없는지를 모든 탐색 돌기 전에 판단해 유망성이 없는 경우를 배제시키면서 탐색을 하는 방식이다.
당연하게도 단순히 모든 노드를 방문하며 탐색하는 DFS, BFS보다 유망성이 없는 경우의 수를 배제시키는 백트래킹(BackTracking) 알고리즘을 사용한 방식이 더 빠르게 실행 가능하다.
코드를 작성후 python3로 제출했으나 시간 초과가 발생했다. visited를 추가해 실행 시간을 단축시켰음에도 시간 초과가 발생해서 구글링해보니 python이 백트래킹 알고리즘을 사용하기에 부적합한 언어이며 pypy3로 제출해야한다는 것을 알 수 있었다. pypy3로 제출하니 정상적을 실행이 완료되었음을 알 수 있다.
import sys
def promising(x):
for i in range(x):
if row[x] == row[i] or abs(row[x] - row[i]) == abs(x - i):
return False
return True
def nqueen(x):
global ans
if x == n:
ans += 1
return
for i in range(n):
if visited[i]:
continue
row[x] = i # [x, i]에 퀸을 위치
if promising(x):
visited[i] = True # i열 퀸 배치 표시
nqueen(x + 1) # i열에 퀸을 배치시킨 후 다음 열로 이동
visited[i] = False # i열 퀸 배치 해제
n = int(sys.stdin.readline())
ans = 0
row = [0] * n
visited = [False] * n
nqueen(0)
print(ans)