Algorithm

SWEA 1954번: 달팽이 숫자 (D2) - Python 풀이

서서리 2025. 4. 21. 23:36
SMALL

문제 설명

 

SW Expert Academy

SW 프로그래밍 역량 강화에 도움이 되는 다양한 학습 컨텐츠를 확인하세요!

swexpertacademy.com

달팽이는 1부터 N*N까지의 숫자가 시계방향으로 이루어져 있다.

다음과 같이 정수 N을 입력 받아 N크기의 달팽이를 출력하시오.

[예제]

N이 3일 경우,

N이 4일 경우,

[제약사항]

달팽이의 크기 N은 1 이상 10 이하의 정수이다. (1 ≤ N ≤ 10)

[입력]

가장 첫 줄에는 테스트 케이스의 개수 T가 주어지고, 그 아래로 각 테스트 케이스가 주어진다.

각 테스트 케이스에는 N이 주어진다.

[출력]

각 줄은 '#t'로 시작하고, 다음 줄부터 빈칸을 사이에 두고 달팽이 숫자를 출력한다.

(t는 테스트 케이스의 번호를 의미하며 1부터 시작한다.)

입력

2
3
4

출력

#1
1 2 3
8 9 4
7 6 5
#2
1 2 3 4
12 13 14 5
11 16 15 6
10 9 8 7

풀이 과정

아무래도 이 문제는 과소평가 당했습니다. D2라니요 ㅋㅋ

SWEA 사이트는 좋은 게 문제마다 댓글을 달 수가 있어서 사람들 반응을 볼 수 있는데 저처럼 생각하시는 분들이 꽤 있어 다행입니다.

일단 규칙 자체는 쉽게 찾을 수 있습니다. arr[0][0]에서 1로 시작해서, 열 1칸씩 증가 & 값 1씩 증가해서 채우다가 벽을 만나면(= N*N 범위를 벗어나면) 방향을 아래로 틉니다.

아래로 이동하면서 또 1씩 증가시키다가 벽을 만나면 왼쪽으로 방향을 틀고, 또 벽을 만나면 오른쪽으로 방향을 틉니다. 즉 방향은 오른쪽 → 아래쪽 → 왼쪽 → 위쪽 순으로 반복됩니다.

값이 N*N이 나오면 칸을 다 채웠으니 종료합니다.

“방향대로 1칸씩 이동, 값 1씩 증가, 범위 벗어나면 방향 전환” 이라는 구조가 반복되니 재귀로 풀고, 방향 전환을 구현하기 위해 dx, dy를 사용합니다.

정답 코드

T = int(input())
     
for test_case in range(1, T + 1):
    N = int(input())
    # 값을 채워야 할 2차원 배열, 0으로 초기화 해서 생성
    arr = [[0] * N for _ in range(N)]
    # 오른쪽, 아래쪽, 왼쪽, 위쪽 순으로 dx, dy 구성
    dx = [0, 1, 0, -1]
    dy = [1, 0, -1, 0]
    
    # 재귀 함수
    def recursive_snail(x, y, d, num):
        # row = x, col = y 위치에 num 값으로 채웁니다
        arr[x][y] = num
        # 방향에 맞게 1칸 이동
        nx = x + dx[d]
        ny = y + dy[d]
         
        # N * N 값까지 채우면
        if num == N * N:
            # 프로그램 종료
            return
        # 0 <= nx < N, 0 <= ny < N 이 범위를 벗어났거나,
        # 범위 안이지만 arr[nx][ny] 값이 이미 채워진 상태라면
        if nx < 0 or nx >= N or ny < 0 or ny >= N or arr[nx][ny] != 0:
            # 방향을 전환합니다
            # 4개의 방향 안에서만 움직여야 하기 때문에 4로 나눈 나머지를 사용
            d = (d + 1) % 4
            # d 제외 지금 값 그대로 재귀 호출
            recursive_snail(x, y, d, num)
        # 범위 안이고, arr[nx][ny] 값이 아직 0이라면
        else:
            # nx, ny, num + 1 값으로 재귀 호출
            recursive_snail(nx, ny, d, num + 1)
         
    # (0, 0)에서 dx[0], dy[0], num = 1로 재귀 시작
    recursive_snail(0, 0, 0, 1)
    print("#%d" %test_case)
    for a in arr:
        # 언패킹 연산자 *로 1차원 배열 a의 요소를 하나씩 풀어서 출력
        print(*a)

이번에도 주석으로 열심히 설명했습니다.

recursive_snail의 2번째 if문에서 arr[nx][ny]가 0이 아닌지를 왜 확인해야 하냐면, 방향 전환을 해야 하는 케이스가 1. N * N 범위를 벗어날 때 2. 이미 채워진 값을 만났을 때로 2가지가 있기 때문입니다.

예를 들어 N = 3일 때, 3 찍고 아래로 방향 전환, 5 찍고 왼쪽으로 전환, 7 찍고 위쪽으로 전환합니다. 이때는 모두 1번의 케이스였습니다. 그 후 8 찍고 오른쪽으로 전환을 해야 하는데, 이때는 nx, ny가 N * N의 범위를 벗어난 게 아닙니다. 계속 위로 갈까 하다가 이미 채워진 ‘1’을 만난 거죠. 이는 2번 arr[nx][ny]가 0이 아닌 경우에 해당하고 이때에도 방향을 전환해 주어야 합니다.

이상입니다.

LIST