[백준] Z

최동혁·2022년 12월 6일
0

백준

목록 보기
39/68

Z

solved_ac[Class3][Z](https://www.acmicpc.net/problem/1074)

문제

한수는 크기가 2^N × 2^N인 2차원 배열을 Z모양으로 탐색하려고 한다. 예를 들어, 2×2배열을 왼쪽 위칸, 오른쪽 위칸, 왼쪽 아래칸, 오른쪽 아래칸 순서대로 방문하면 Z모양이다.

image

N > 1인 경우, 배열을 크기가 2^(N-1) × 2^(N-1)로 4등분 한 후에 재귀적으로 순서대로 방문한다.

다음 예는 2^2 × 2^2 크기의 배열을 방문한 순서이다.

image

N이 주어졌을 때, r행 c열을 몇 번째로 방문하는지 출력하는 프로그램을 작성하시오.

다음은 N=3일 때의 예이다.

image

입력

첫째 줄에 정수 N, r, c가 주어진다.

출력

r행 c열을 몇 번째로 방문했는지 출력한다.

제한

  • 1 ≤ N ≤ 15
  • 0 ≤ r, c < 2^N

예제 입력 1

2 3 1

예제 출력 1

11

예제 입력 2

3 7 7

예제 출력 2

63

예제 입력 3

1 0 0

예제 출력 3

0

예제 입력 4

4 7 7 

예제 출력 4

63

예제 입력 5

10 511 511

예제 출력 5

262143

예제 입력 6

10 512 512

예제 출력 6

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이다.

  • 이걸 점화식으로 풀어내면

    • X1 = X0 * 4 + 3 이 된다.
    • 전 좌표에서 4배를 해준 후 3을 더해주면 그 다음 좌표가 나오는 것이다.
  • 그렇게 해서 (7,6)과 (6,7), (6,6)을 보자

  • (7,6)의 값(62) -> 몫 (3,3)의 값(15) 나머지 (1,0) -> 몫 (1,1)의 값(3) 나머지 (1,1) -> 몫 (0,0)의 값(0) 나머지 (1,1)

    • 첫번째는 2로 나누었을때 나머지가 1,0인 것의 규칙을 찾아야한다.
      • 62 -> 15로 내려왔는데 이렇게 되면 15*4 + 2가 된다.
    • 두번째는 2로 나누었을때 나머지가 1,1인 것의 규칙이다.
      • 이건 위에서 찾았듯이 4배를 해준 후 3을 더해주는것이다.
  • (6,7)의 경우를 보면 (6,7) -> (3,3), 나머지(0,1)이다. 값은 61 -> 15이다.

    • 나머지가 (0,1)인 경우는 15*4 + 1이다.
  • (6,6)의 경우는 (6,6) -> (3,3), 나머지(0,0)이다. 값은 60 -> 15이다.

    • 나머지가 (0,0)의 경우는 15*4 + 0이다.

규칙 결론

  • 좌표를 2로 나누었을때

    • 나머지가 (1,1)인 경우
      • X1 = X0 * 4 + 3
    • 나머지가 (1,0)인 경우
      • X1 = X0 * 4 + 2
    • 나머지가 (0,1)인 경우
      • X1 = X0 * 4 + 1
    • 나머지가 (0,0)인 경우
      • X1 = X0 * 4 + 0
  • 이렇게 깔끔하게 떨어진다.

  • 여기서 우리가 graph의 값을 주지 않은채로 정답을 도출해내려면 어떡해야 할까?

  • 0,0의 좌표값이 0인 것은 알고있다. 그렇다면 나누어서 좌표가 0,0이 될때까지 재귀적 호출을 통해 풀어내면 된다.

첫번째 예시

  1. (7,7)
  2. (3,3) 나머지 (1,1)
  3. (1,1) 나머지 (1,1)
  4. (0,0) 나머지 (1,1)
  • 이렇게 재귀적 호출을 하면 4번부터 다시 거슬러 올라가면서 계산을 하게 된다.
  1. (0,0)의 좌표값은 0 나머지는 (1,1)이니깐 0 * 4 + 3 = 3 을 리턴해준다.
  2. return = 3 * 4 + 3 = 15
  3. return = 15 * 4 + 3 = 63
  4. 정답 = 63

두번째 예시

  1. (7,6)

  2. (3,3) 나머지 (1,0)

  3. (1,1) 나머지 (1,1)

  4. (0,0) 나머지 (1,1)

  5. return = 0 * 4 + 3 = 3

  6. return = 3 * 4 + 3 = 15

  7. return = 15 * 4 + 2 = 62

  8. 정답 = 62

  • 여기서 더 간편하게 하고 싶다면??

  • 나머지가 (1,0)일때는 2를 더해주고 (0,1)일때는 1을 더해주고 (1,1)일때는 3을 더해준다. 그렇다면 x좌표의 나머지에 2를 곱하고 y좌표를 더해준것과 같게 된다.

  • 이게 무슨 말이냐면

    • 3 = (1 * 2) + 1 나머지가 (1,1)
    • 2 = (1 * 2) + 0 나머지가 (1,0)
    • 1 = (0 * 2) + 1 나머지가 (0,1)
    • 0 = (0 * 0) + 0 나머지가 (0,0)

슈도 코드

  • 재귀 함수 정의
  • 좌표값(r, c)을 파라미터로 받는다.
    • 만약 좌표가 (0,0)이 되면
      • 더이상 함수를 호출하지 않고 (0,0)의 값인 0을 리턴해준다.
    • 각 좌표를 2로 나눈 몫과 나머지로 정리한다.
    • 2로 나눈 몫을 재귀함수의 좌표로 호출을 하고 그 값의 4배를 해준 후 x좌표의 나머지에 2배, y좌표의 나머지에 1배를 한 것을 더해준다.
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))

결론

이런식으로 그래프들이 전부 다 입력을 받은 상태로 좌표에 해당하는 값을 도출해내는 문제는 무지성으로 그래프에 값들을 전부 넣어놓고 생각할 것이 아닌 적당한 규칙이 있는지부터 먼저 찾아내는것이 중요하다.

profile
항상 성장하는 개발자 최동혁입니다.

0개의 댓글

관련 채용 정보