[백준 삼성기출 △] 원판 돌리기(python)

이진규·2022년 10월 10일
1

백준(PYTHON)

목록 보기
108/115

문제

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

나의 코드

"""

"""

from collections import deque

N, M, T = map(int, input().split()) # N:반지름, M:정수의 개수, T:회전 횟수
circle = [ deque(map(int, input().split())) for _ in range(N) ] # 원판
spin = [ list(map(int, input().split())) for _ in range(T) ] # 회전 정보


def circle_rotate(x, d): # 원판을 회전 시켜주는 함수

    for i in range(x, N+1, x): # x의 배수 만큼 원판 돌리기

        if d == 0: # d=0이면 시계방향 회전
            circle[i-1].rotate(1)
        else: # d=-1이면 반시계방향 회전
            circle[i-1].rotate(-1)

dx, dy = [0, 0, -1, 1], [-1, 1, 0, 0]
def circle_remove(r, c, num): # 인접한 곳에 같은 수가 2개 이상이면 수를 지우는 함수
    global flag

    visited[r][c] = True
    q = deque([(r, c)])
    remove_list = [(r, c)]

    while q:
        px, py = q.popleft()

        for k in range(4):
            mx = (px + dx[k])
            my = (py + dy[k]) % M # ★ M의 나머지를 통해 첫번째 열과 마지막 열을 연결 시킴 ★

            if 0 <= mx < N and 0 <= my < M:
                if circle[mx][my] == num and not visited[mx][my]:
                    visited[mx][my] = True
                    remove_list.append((mx, my))
                    q.append((mx, my))

    if len(remove_list) >= 2: # 만약 인접한 수가 2개 이상이라면 지움
        for a, b in remove_list:
            circle[a][b] = 0 # 지운 곳은 0으로 기록
        flag = True


def circle_avg(): # 수를 지우지 못한 경우 평균을 구해서 평균보다 작은경우 +1, 큰 경우 -1을 하는 함수

    total = 0
    cnt = 0
    for i in range(N):
        for j in range(M):
            if circle[i][j] > 0:
                total += circle[i][j]
                cnt += 1

    if cnt != 0: # ★ 만약 아무 수가 없다면 cnt가 0이라 zerodivision이 되므로 조심하기 ★
        avg = total / cnt
        
    for i in range(N):
        for j in range(M):
            if circle[i][j] > 0:
                if circle[i][j] > avg:
                    circle[i][j] -= 1
                elif circle[i][j] < avg:
                    circle[i][j] += 1


for x, d, k in spin: # x의 배수의 원판, d:0->시계 1->반시계, k:돌아가는 칸수

    # 1. 번호가 xi의 배수인 원판을 di방향으로 ki칸 회전시킨다. di가 0인 경우는 시계 방향, 1인 경우는 반시계 방향이다.
    for _ in range(k): # k칸 만큼 회전
        circle_rotate(x, d)

    # print(circle)

    # 2. 원판에 수가 남아 있으면, 인접하면서 수가 같은 것을 모두 찾아 지운다.
    visited = [ [False] * M for _ in range(N) ]
    flag = False # 원판에서 수를 지웠는지 체크하는 변수
    for i in range(N):
        for j in range(M):
            if circle[i][j] > 0:
                circle_remove(i, j, circle[i][j])

    # print(circle, flag)

    # 2-1. 인접하면서 같은 수가 없는 경우에는 원판에 적힌 수의 평균을 구하고, 평균보다 큰 수에서 1을 빼고, 작은 수에는 1을 더한다.
    if flag == False:
        circle_avg()

answer = sum( sum(x) for x in circle )
print(answer)
    

설명

  1. deque()의 rotate() -> rotate()안에 인자가 양수이면 시계 방향, 음수이면 반시계 방향 회전임.

  2. circle_remove() 즉, BFS 부분에서 열 끼리 연결 시키기 위해 % M을 해줌. 이 부분 깊게 생각안하면 놓치기 쉬우므로 주의

    my = (py + dy[k]) % M # ★ M의 나머지를 통해 첫번째 열과 마지막 열을 연결 시킴 ★

참고 자료

profile
항상 궁금해하고 공부하고 기록하자.

0개의 댓글