solved_ac[Class3][Z](https://www.acmicpc.net/problem/1074)
한수는 크기가 2^N × 2^N인 2차원 배열을 Z모양으로 탐색하려고 한다. 예를 들어, 2×2배열을 왼쪽 위칸, 오른쪽 위칸, 왼쪽 아래칸, 오른쪽 아래칸 순서대로 방문하면 Z모양이다.
N > 1인 경우, 배열을 크기가 2^(N-1) × 2^(N-1)로 4등분 한 후에 재귀적으로 순서대로 방문한다.
다음 예는 2^2 × 2^2 크기의 배열을 방문한 순서이다.
N이 주어졌을 때, r행 c열을 몇 번째로 방문하는지 출력하는 프로그램을 작성하시오.
다음은 N=3일 때의 예이다.
첫째 줄에 정수 N, r, c가 주어진다.
r행 c열을 몇 번째로 방문했는지 출력한다.
2 3 1
11
3 7 7
63
1 0 0
0
4 7 7
63
10 511 511
262143
10 512 512
786432
문제의 수학적 규칙을 찾고 재귀 함수를 이용하면 쉽게 풀 수 있는 문제이다. 이런 문제는 보통 graph를 이용하여 전부 다 숫자를 집어넣어서 graph의 일정 부분을 빼내는 것 보다 graph를 이용하지 않고 숫자들의 규칙을 찾아서 단순 연산을 통해 풀어내는게 일반적이다. 필자는 처음에 규칙을 찾지 못해서 2차원 배열을 만들어서 재귀를 이용하여 풀려고 했지만 포기하고 규칙을 찾았다.
예를 들어 7,7을 찾고 싶다고 해보자.
x좌표와 y좌표를 2로 나눈 수는 3,3이다. 나머지는 각 1,1이다.
그리고 3,3에서 2로 나눈 수는 1,1이며 나머지는 각 1,1이다.
1,1에서 2로 나눈 수는 0,0이며 나머지는 각 1,1이다.
자 이제 여기서 규칙을 찾아보자.
0,0의 값은 0이다. 1,1은 3이며 3,3은 15, 7,7은 63이다.
여기서 나머지들은 전부 1,1의 쌍을 이룬다.
2로 나누었을때 나머지가 전부 1인 좌표들인데 0, 3, 15, 63이다.
이걸 점화식으로 풀어내면
그렇게 해서 (7,6)과 (6,7), (6,6)을 보자
(7,6)의 값(62) -> 몫 (3,3)의 값(15) 나머지 (1,0) -> 몫 (1,1)의 값(3) 나머지 (1,1) -> 몫 (0,0)의 값(0) 나머지 (1,1)
(6,7)의 경우를 보면 (6,7) -> (3,3), 나머지(0,1)이다. 값은 61 -> 15이다.
(6,6)의 경우는 (6,6) -> (3,3), 나머지(0,0)이다. 값은 60 -> 15이다.
규칙 결론
좌표를 2로 나누었을때
이렇게 깔끔하게 떨어진다.
여기서 우리가 graph의 값을 주지 않은채로 정답을 도출해내려면 어떡해야 할까?
0,0의 좌표값이 0인 것은 알고있다. 그렇다면 나누어서 좌표가 0,0이 될때까지 재귀적 호출을 통해 풀어내면 된다.
첫번째 예시
두번째 예시
(7,6)
(3,3) 나머지 (1,0)
(1,1) 나머지 (1,1)
(0,0) 나머지 (1,1)
return = 0 * 4 + 3 = 3
return = 3 * 4 + 3 = 15
return = 15 * 4 + 2 = 62
정답 = 62
여기서 더 간편하게 하고 싶다면??
나머지가 (1,0)일때는 2를 더해주고 (0,1)일때는 1을 더해주고 (1,1)일때는 3을 더해준다. 그렇다면 x좌표의 나머지에 2를 곱하고 y좌표를 더해준것과 같게 된다.
이게 무슨 말이냐면
import sys
N, r, c = map(int, sys.stdin.readline().split())
def rec(r, c):
if r == 0 and c == 0:
return 0
a = r % 2
b = c % 2
r = r // 2
c = c // 2
return rec(r, c)*4 + 2*a + b
print(rec(r, c))
이런식으로 그래프들이 전부 다 입력을 받은 상태로 좌표에 해당하는 값을 도출해내는 문제는 무지성으로 그래프에 값들을 전부 넣어놓고 생각할 것이 아닌 적당한 규칙이 있는지부터 먼저 찾아내는것이 중요하다.