Notice
Recent Posts
Recent Comments
Link
«   2025/07   »
1 2 3 4 5
6 7 8 9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 29 30 31
Tags
more
Archives
Today
Total
관리 메뉴

성장기록지

클린 구현 프로젝트 2) 백준 16926 배열 돌리기 본문

알고리즘

클린 구현 프로젝트 2) 백준 16926 배열 돌리기

pengcon 2024. 5. 24. 11:23

문제

https://www.acmicpc.net/problem/16926

문제 해결

배열을 조건에 맞게 한칸씩 반시계 방향으로 돌리는 문제이다.

가로와 세로의 길이 중 작은값은 항상 짝수라는 조건이 있어서 

가로 세로가 둘다 홀수일때의 예외처리를 해주지 않아도 되어서 좋았다.

만약 둘 다 홀수인 아래의 그림(N=5,M=7) 보면, 17~19까지 반시계로 회전을 할 수 없게 된다. 

 

 

구조 설계

많은 변수와 함수가 필요하다고 생각되어 하나씩 설계해 나갔다.

초기의 배열을 의미하는 nums ,

회전 후 정답 배열을 의미하는 ans 를 구성하였고,

가로와 세로의 길이가 각자 다를 수 있으므로,

x와 y가 배열을 순회할때 가능한 시작점과 끝점을 의미하는

x_min, x_max 와 y_min, y_max 변수를 만들었다.

그 후 위의 그림처럼 빨간색 영역을 extract_edges 함수를 통해 deque에 저장하고,

덱의 rotation 기능을 통해 반시계방향으로 회전하도록 한 후,

fill_edges 함수를 통하여 정답 리스트에 값을 넣도록 하였다.

그 후 x,y의 min과 max를 1씩 늘리고 줄여 다음 영역(파란색)에서 동일한 동작을 하도록 하였다. 

 

 

 

 

 

전체적인 코드는 다음과 같다.

from collections import deque
import sys
input = sys.stdin.readline

#입력받기
def read_input():
    N, M, R = map(int, input().split())
    nums = [list(map(int, input().split())) for _ in range(N)]
    return N, M, R, nums

#배열을 추출
def extract_edges(nums, x_min, x_max, y_min, y_max):
    temp = deque()
    count = 0
    # 위 배열
    for i in range(y_min, y_max + 1):
        temp.append(nums[x_min][i])
        count += 1
    # 오른쪽 배열
    for j in range(x_min + 1, x_max):
        temp.append(nums[j][y_max])
        count += 1
    # 아래 배열
    for k in range(y_max, y_min - 1, -1):
        temp.append(nums[x_max][k])
        count += 1
    # 왼쪽 배열
    for l in range(x_max - 1, x_min, -1):
        temp.append(nums[l][y_min])
        count += 1
    return temp, count

#회전한 값대로 배열 채우기
def fill_edges(ans, temp, x_min, x_max, y_min, y_max):
    # 위 배열
    for i in range(y_min, y_max + 1):
        ans[x_min][i] = temp.popleft()
    # 오른쪽 배열
    for j in range(x_min + 1, x_max):
        ans[j][y_max] = temp.popleft()
    # 아래 배열
    for k in range(y_max, y_min - 1, -1):
        ans[x_max][k] = temp.popleft()
    # 왼쪽 배열
    for l in range(x_max - 1, x_min, -1):
        ans[l][y_min] = temp.popleft()

def rotate_matrix(N, M, R, nums):
    ans = [[-1 for _ in range(M)] for _ in range(N)]

    x_min, y_min = 0, 0
    x_max, y_max = N - 1, M - 1

    while x_max > x_min and y_max > y_min:
        temp, count = extract_edges(nums, x_min, x_max, y_min, y_max)
        this_R = R % count
        temp.rotate(-this_R)
        #회전한 배열을 다시 채워넣는다.
        fill_edges(ans, temp, x_min, x_max, y_min, y_max)
        x_min += 1
        y_min += 1
        x_max -= 1
        y_max -= 1
    
    return ans

N, M, R, nums = read_input()
ans = rotate_matrix(N, M, R, nums)
for row in ans:
    print(*row)

설계상의 디테일

deque의 rotate

리스트의 크기만큼 rotate를 하면 원점으로 돌아오므로 시간낭비라고 생각하여

this_R = R% count로 횟수를 줄였다.

기능별 함수 분리

기능들을 각각 함수로 분리하여

최대한 읽기 쉬운 코드를 구현하려고 노력하였다.

 

 

추가로 생각해볼 점

위 배열, 오른쪽 배열, 아래 배열, 왼쪽배열을 

순회하는 코드도 함수로 만들어야 할까

파라미터가 너무 많이필요할거같아 오히려 가독성이 안좋을 수 있을거같아서

따로 함수로 구분하진 않았다. 

조금 더 고민해보고 더 깔끔한 코드로 구현해야겠다.