[골드4] https://www.acmicpc.net/problem/9663
해설 없이 스스로 풀었나요?
X
대략적인 구조는 생각하였으나 일차원배열, 대각선위치에 대해 명확히 구현하지 못함
재풀이 필요
N-Queen 문제는 크기가 N × N인 체스판 위에 퀸 N개를 서로 공격할 수 없게 놓는 문제이다.
N이 주어졌을 때, 퀸을 놓는 방법의 수를 구하는 프로그램을 작성하시오.
입력
첫째 줄에 N이 주어진다. (1 ≤ N < 15)
출력
첫째 줄에 퀸 N개를 서로 공격할 수 없게 놓는 경우의 수를 출력한다.
예제 입력 1
8
예제 출력 1
92
체스에서 퀸은 상하좌우, 대각선으로 이동할 수 있다.
문제에서는 N x N의 체스판에 N개의 퀸을 서로 공격할 수 없도록 놓는 것을 요구하고 있다.
먼저 퀸은 한 행에 하나 밖에 못 둔다. 한 행에 두개의 퀸이 위치한다면 좌 또는 우로 이동해 이미 서로 공격이 가능하기 때문이다. 이러한 이유로 2차원 배열이 아닌, 1차원 배열을 통해 퀸의 위치를 기록할 수 있다.
배열의 인덱스를 행번호, 배열 값을 열번호로 사용한다.
예를 들어 chess[1] = 2이면, 1행 2열에 퀸이 위치하는 것이다.
chess = [1, 3, 0, 2]이면 4 x 4 체스판에서 퀸의 위치가 아래와 같이 검정색인 것이다.
⚪⚫⚪⚪
⚪⚪⚪⚫
⚫⚪⚪⚪
⚪⚪⚫⚪
퀸의 위치를 정한 후, 해당 위치에 퀸을 놓을 수 있는지 없는지 isPromising 함수를 통해 판단한다.
우리는 체스판의 가장 윗 행부터 퀸을 놓고 있으므로 isPromising에서 검사할 때 x번째 행까지만 검사하면 된다.
아래 두 조건에 걸리면, 퀸을 놓을 수 없다.
1. 같은 열에 이미 다른 퀸이 있는 경우
퀸의 위치를 담고 있는 chess 배열에 현재 위치시킨 퀸의 위치와 같은 값이 있는지, 즉 chess[x] = i 에서의 i값 (=열의 값)이 같은 게 있는지 확인한다. 만약 같은 값이 있다면 이미 같은 열에 다른 퀸이 있다는 것이므로 가지치기를 한 후, 이전으로 되돌아가는 백트래킹을 한다.
2. 왼쪽 대각선 혹은 오른쪽 대각선에 다른 퀸이 있는 경우
대각선에 퀸이 위치했는지는 인덱스 관계를 통해 파악할 수 있다.
예를 들어 현재 퀸을 놓은 위치가 (3,3)이라 하고 살펴보자.
왼쪽 위 대각선을 살펴보면
왼쪽 위 대각선 좌표는 각각 (2,2),(1,1),(0,0)이 된다.
여기서 (3,3)을 각각 i,j라 하고, (2,2)를 x1, y1, (1,1)을 x2, y2, (0,0)을 x3, y3이라고 하자.
i에서 x1을 뺀 값과 j에서 y1을 뺀 값이 모두 1로 같다.
또한 i에서 x2를 뺀 값과 j에서 y2를 뺀 값이 모두 2로 같고, 세번째도 3으로 같다.
오른쪽 위 대각선 좌표는 각각 (2,4), (1,5), (0,6)이 된다.
왼쪽 위 대각선과 마찬가지로 빼보면 각각 (1,-1), (2,-2), (3,-3)으로 절댓값을 취하면 값이 같은 것을 볼 수 있다.
이렇게 대각선 상에 위치했는지 확인하는 것은 각 좌표의 행의 차와 열의 차의 절댓값이 같은지 확인하면 알 수 있다.
이 두가지 조건에 걸리지 않으면, 즉 현재 퀸을 놓으려는 위치가 promising 하다면 True를 반환하여 nQueen(x+1)을 호출해 다음 행에 대해서도 퀸을 놓는 것을 진행하면 되고, 만약 재귀호출을 진행하다가 현재 행의 위치 x가 n이 되면 모든 퀸을 놓았음을 뜻하므로 count 값을 1 증가시키면 된다.
import sys
def nQueen(x):
global count
if x == n: # 다 놓았다면
count += 1
# print(chess)
return
for i in range(n):
chess[x] = i # (x, i)에 퀸 놓음
if isPromising(x):
nQueen(x+1) # 다음 행에 대해 놓아보기
def isPromising(x):
for i in range(x): # x번째 행까지
# 상하좌우에 위치 = 열 값이 같음 [0, 2, 2, ...] / 대각선 상에 위치 = 행의 차와 열의 차가 같음 [2, 0, 1]
if chess[x] == chess[i] or abs(x - i) == abs(chess[x] - chess[i]):
return False
return True
n = int(sys.stdin.readline().rstrip())
chess = [0] * n
count = 0
nQueen(0)
print(count)
심지어 .. 처음에 입력받는 부분을 input()으로 하여 PyPy3로 제출했을 때도 시간초과가 떴다. sys.stdin.readline().rstrip()으로 바꿔주고나서야 정답처리가 되었다.
재귀호출이 등장해 재귀깊이를 다시 설정하는 것도 해봤지만, 여전히 python에서는 시간초과였다..!
덧붙여 퀸의 위치를 나타내는 chess 배열의 초기화에 대해서도 고민했는데, 생각해보니 방문여부 같은 것을 따지지 않고 for문을 돌며 작은 값부터 큰 값까지 값을 계속 업데이트 해주므로, 배열 초기화는 고려하지 않아도 되었다.
abs(x - i) == abs(chess[x] - chess[i])
이와 같이 두 좌표의 행의 차와 열의 차를 고려해야 하는데,
몇가지 예시만 생각하고 하나의 좌표에서 행과 열의 차이를 구한 뒤 그 값을 비교하는 것으로 구현하였다.
즉 abs(x - chess[x]) == abs(i - chess[i]) 이렇게 구현하였는데
''(0,2) (1,3)에서는 하나의 좌표에서 행과 열의 차이가 각각 2, 2이므로 ..!''
당장 (2,2) (1,3)에서만 비교해도 하나의 좌표에서 행과 열의 차이가 각각 0, 2이므로 이는 잘못된 것임을 알 수 있다.