BOJ 29984 Onix

BOJ 29984 Onix

문제 링크:

문제 내용

n×nn \times n의 사각 격자가 있습니다. 이 격자의 맨 왼쪽 위 칸에서 시작하여 모든 칸을 한 번씩 밟고 맨 왼쪽 아래 칸에서 끝나는 서로 다른 경로의 개수를 구하세요.

입력

첫 줄에 nn이 주어집니다. (1n81 \le n \le 8)

출력

문제의 정답을 출력합니다. 정답은 2642^{64}보다 작음이 보장됩니다.

문제 풀이

스포일러

이 문제는 경로를 관리하는 커넥션 프로파일 DP로 풀 수 있습니다.

임의의 경로를 하나 잡고, 사각 격자 전체를 다음의 모양으로 잘랐다고 가정해 봅시다.

??????

이렇게 잘랐을 때 경계선의 칸 수는 n+1n+1칸(그림의 ?의 개수)이고, 이러한 n+1n+1칸 각각에 대해 경로가 그 칸을 통과할 수도 있고, 통과하지 않을 수도 있습니다. 맨 왼쪽 위 칸에서 시작하여 맨 왼쪽 아래 칸에서 끝나는 경로를 이렇게 자르면, 경로의 부분들 중에서 그림에 표시된 영역에 포함되는 것은 맨 왼쪽 위 칸에서 시작해서 끊어진 경로 하나가 있고, 추가로 양 끝이 끊어진 부분 경로가 0개 이상이 있게 됩니다.

이제 각 경계선의 상태를 적절한 방법으로 나타낼 것입니다. 이를 위해, 왼쪽 변에서 시작하여 경계선을 따라 경계선의 각 칸에 1부터 n+1n+1까지 순서대로 번호를 붙입니다. 그리고 다음의 방법으로 길이 n+1n+1의 배열 a1,a2,,an+1a_1, a_2, \cdots, a_{n+1}을 만들 수 있습니다.

  • 맨 왼쪽 위 칸에 연결된 경로가 지나가는 경계 ii에 대해서, ai=ia_i = i로 둡니다.
  • 경계 ii를 통해 들어간 부분 경로가 경계 jj를 통해 나오거나 그 반대라면, ai=j,aj=ia_i = j, a_j = i로 둡니다.
  • 나머지 경계는 ai=0a_i = 0으로 둡니다.

예를 들어, 다음의 경우에 이 배열은 [0, 0, 3, 0, 6, 5]가 됩니다.

이제 전이를 설계해야 합니다. 전이는 다음의 그림과 같이 한 칸 단위로, 왼쪽에서 ii번째 칸을 처리할 때 aia_iai+1a_{i+1}, 그리고 이들과 연결된 경로의 끝점들의 상태를 업데이트하는 방식으로 진행됩니다.

ii+1i'i+1'

이 문제의 경우, 구체적으로 다음의 과정을 통해 전이를 할 수 있습니다.

  • ai0a_i \ne 0, ai+10a_{i+1} \ne 0인 경우
    • 둘 모두 어떤 경로의 끝점이므로 둘을 서로 연결해야 합니다. 그 결과 ai=ai+1=0a_i' = a_{i+1}' = 0이 됩니다.
    • ai=ia_i = i였다면 i+1i+1과 연결된 경로의 반대쪽 끝점, 즉 aai+1a_{a_{i+1}}ai+1a_{i+1}로 수정하여 출발점에서 연결된 경로임을 표시해 줍니다.
    • ai+1=i+1a_{i+1} = i+1이었다면 마찬가지로 처리합니다.
    • ai=i+1,ai+1=ia_i = i+1, a_{i+1} = i였다면 둘을 연결하면 닫힌 루프가 만들어지므로 전이하지 않습니다.
    • 그렇지 않은 경우, ii의 반대쪽과 i+1i+1의 반대쪽을 서로 연결해 줍니다.
  • ai=0a_i = 0인 경우
    • i+1i+1에서 연결된 부분 경로는 ii'로 나가거나 i+1i+1'로 나갈 수 있습니다.
    • 두 가지 가능성에 대해 적절히 처리해 주고, i+1i+1의 반대쪽도 (존재한다면) 업데이트해 줍니다.
  • ai+1=0a_{i+1} = 0인 경우도 마찬가지로 구현해 줍니다.
  • 둘 다 0인 경우
    • 이 경우에는 새로 생기는 두 경계를 잇는 새로운 경로를 만들어야 합니다.
    • ai=i+1a_{i} = i+1, ai+1=ia_{i+1} = i로 만들어주면 됩니다.

한 칸씩 처리하여 한 줄의 끝에 도달했다면, 현재의 경계는 ___| 모양일 것입니다. 여기서 다음 줄로 넘어갈 때는 |¯¯¯ 모양으로 만들어야 하는데, 다음과 같이 처리해 주면 됩니다.

  • 맨 마지막 칸을 통과하는 경로가 있다면 무시합니다.
  • 그렇지 않다면, 나머지 칸의 값들을 한 칸씩 오른쪽으로 밀고, 0이 아닌 모든 값들에 1을 더해주면 모든 연결성이 유지됩니다.

맨 왼쪽 위 칸에서 시작하여 맨 왼쪽 아래 칸에서 끝나는 경로는, 맨 왼쪽 위 칸의 바로 위에서 시작하여 맨 왼쪽 아래 칸의 바로 아래에서 끝나는 것으로도 볼 수 있으므로, 시작 상태와 끝 상태를 모두 a2=2,ai(i2)=0a_2 = 2, a_i (i \ne 2) = 0으로 둡니다.

마지막으로 DP 테이블을 해시맵으로 만들어서 각 상태의 경우의 수를 저장해 주면 시간 내에 충분히 통과할 수 있습니다. 잘 구현했다면 파이썬으로 n=11n = 11까지도 수 초 내에 구해집니다.

Last updated on