[백준] 16926번: 배열 돌리기 1

Chaejung·2023년 5월 6일

알고리즘_Python

목록 보기
20/22
post-thumbnail

문제

코딩테스트 스터디에서 모각코 때 선정된 문제이다.
문제 이해는 빠르게 됐으나, 역시나 코드로 구현하는 것은 30분만에 하긴 쉽지 않았다.
스터디가 오후 10시 반에 끝났는데, 새벽 1시 쯤까지 하니 다 풀 수 있었다!
역시 음주 코딩은 쉽지 않군, 디버깅 속도가 2배는 느려지는 것 같다.

풀이 전 생각했던 방향

구현은 대체로 입력 제한에서 풀이 방식을 효율적으로 구성할 수 있는 힌트가 있다.
그래서 특이하게 보이는 것을 잘 찾는 게 중요하다.
이 문제의 경우에서는 세번째 제한, 바로 가로, 세로 중 작은 것이 무조건 짝수라는 것이다.

그래서 문제를 다시 보니, 배열이 돌려지는 묶음이 루프 형태이고, 닫힌 형태가 보장되는 것을 알 수 있었다, 즉 한 줄 짜리 배열은 없고 무조건 사각형 형태로 구성되며, 해당 배열이 시계 반시계 방향으로 돌아간다는 것이다.

그래서 2차원 배열에서 동일한 묶음으로 돌아가는 배열의 위치를 따로 추출한 뒤, 해당 배열을 회전 수만큼 pop, push를 반복하여 회전 후 위치를 다시 2차원 배열로 만들고, 주어진 배열의 숫자를 재배치하면 되는 것으로 구현 방향을 설정했다.

풀이

N, M, R = map(int, input().split())

arrayInfo = []

# 주어진 배열을 2차원 형태로 구성하기
for _ in range(N):
    row = list(map(int, input().split()))
    arrayInfo.append(row)

# 한 묶음 배열의 크기와 시작점을 알았을 때, 
# 시작점을 기준으로 배열로 구성되는 좌표를 배열 형태로 반환
# 예를 들어 N, M = [2, 4]이고, (2, 2)가 시작점일 때,
# => [(2, 2), (3, 2), (3, 3), (3, 4), (3, 5), (2, 5), (2, 4), (2, 3)]
# 참고로 배열 묶음의 시작은 항상 (k, k)를 만족
def makeLoopPoints(N, M, startPoint):
    loopPointList = []
    x, y = startPoint
    for i in range(N-1):
        loopPointList.append((i+x, 0+y))
    for j in range(M-1):
        loopPointList.append((N-1+x, j+y))
    for i in range(N-1, 0, -1):
        loopPointList.append((i+x, M-1+y))
    for j in range(M-1, 0, -1):
        loopPointList.append((0+x, j+y))
    return loopPointList

# 한 묶음의 배열씩 모아 바깥에서 시작하는 배열 묶음의 리스트 반환
def makeLoopList(N, M):
    loopList = []
    startPoint = (0, 0)
    isNSmaller = True if N<=M else False
    if isNSmaller:
        for i in range(N, 0, -2):
            loopList.append(makeLoopPoints(i, M, startPoint))
            startPoint = (startPoint[0]+1, startPoint[1]+1)
            M -= 2
    else:
        for i in range(M, 0, -2):
            loopList.append(makeLoopPoints(N, i, startPoint))
            startPoint = (startPoint[0]+1, startPoint[1]+1)
            N -= 2
    return loopList

# 배열 묶음을 가지고 회전 수만큼 위치 회전
def rotateLoopList(loopList, count):
    result = []
    for loopPoints in loopList:
        for _ in range(count):
            poppedElement = loopPoints.pop()
            loopPoints.insert(0, poppedElement)
        result.append(loopPoints)
    return result

rotatedResult = rotateLoopList(makeLoopList(N, M), R)

# 회전된 배열의 위치를 초기의 배열에 적용
def putPointToList(rotatedLoopList, N, M):
    originLoopList = makeLoopList(N, M)

    newPointList = [[(-1, -1) for _ in range(M)] for _ in range(N)]
    for loopPointsIdx in range(len(originLoopList)):
        for originPointIdx in range(len(originLoopList[loopPointsIdx])):
            x, y = originLoopList[loopPointsIdx][originPointIdx]
            newPointList[x][y] = rotatedLoopList[loopPointsIdx][originPointIdx]
    return newPointList

rotatedPointResult = putPointToList(rotatedResult, N, M)

# 초기의 배열이 회전된 형태를 2차원 형태로 반환
def makeRotatedList(origin, rotatedPointList, N, M):
    newList = [[0 for _ in range(M)] for _ in range(N)]
    for rowIdx in range(len(rotatedPointList)):
        for colIdx in range(len(rotatedPointList[0])):
            x, y = rotatedPointList[rowIdx][colIdx]
            newList[rowIdx][colIdx] = origin[x][y]
    return newList

# 2차원 형태의 배열을 row 별로 출력하기 위해 반복문 
answer = makeRotatedList(arrayInfo, rotatedPointResult, N, M)

for answerRow in answer:
    print(*answerRow, end='\n')

profile
프론트엔드 기술 학습 및 공유를 활발하게 하기 위해 노력합니다.

0개의 댓글