Coding Test/Python

[백준] 17144번: 미세먼지 안녕! (골드4, 파이썬)

lim.dev 2024. 1. 5. 17:42

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

 

17144번: 미세먼지 안녕!

미세먼지를 제거하기 위해 구사과는 공기청정기를 설치하려고 한다. 공기청정기의 성능을 테스트하기 위해 구사과는 집을 크기가 R×C인 격자판으로 나타냈고, 1×1 크기의 칸으로 나눴다. 구사

www.acmicpc.net

 

아이디어

구현 문제이다.

  • 먼지는 동시에 확산되기 때문에 모든 먼지 좌표를 먼저 큐에 넣고, 그 후 확산 값을 계산해서 업데이트 해주었다. 
  • 공기청정기가 작동하는 부분은 방향 벡터와 무한 반복문을 사용해서 구현했다. 공기청정기를 기준으로 상하로 나누고, 각 좌표를 현재 방향(d)으로 이동하면서 더이상 이동할 수 없으면 방향 벡터를 시계/반시계 방향으로 회전한 뒤 다시 전진하는 코드를 작성했다.

아래는 문제를 풀기 전 스케치한 내용이다.

BFS(): 먼지 확산 시키기

우선 현재 맵에 있는 모든 먼지 좌표가 필요했다. 그래서 아래와 같은 메서드를 만들어 사용했다.

def getDustPos():
    dusts = []
    for i in range(R):
        for j in range(C):
            if board[i][j] > 0:
                dusts.append((i,j, board[i][j]))
    
    return dusts

구한 먼지들을 큐에 넣고 반복을 시작하였다. 탐색을 하지 않기 때문에 bfs라고 부르면 안되지만, 구조가 닮았기 때문에 메서드명을 bfs라고 했다...

def bfs(R, C):
    global board
    queue = deque(getDustPos())

    while queue:
        x, y, v = queue.popleft()

        cnt = 0
        tmp = v // 5
        for d in dv:
            nx = x + d[0]
            ny = y + d[1]

            if nx < 0 or nx >= R or ny < 0 or ny >= C or board[nx][ny] == -1:
                continue
            
            board[nx][ny] += tmp
            cnt += 1
        
        board[x][y] -= tmp * cnt

코드에서 먼지의 좌표 외에 먼지의 값(v)을 저장하는 이유는, 확산의 영향을 받은 값을 사용할 수 없기 때문이다. 초기의 값으로 확산시켜준다.

 

up_cleaner(), down_cleaner(): 공기청정기 작동

스케치대로 방향을 사용하였다.

 

우선 방향에 맞게 벡터 리스트를 만들어준 뒤, 

dv = [(-1, 0), (0,1), (1,0), (0,-1)]

 

공기청정기를 기준으로 위쪽, 아래쪽으로 나누어서 구해주었다.

def up_cleaner(x, y):
    global board
    v, d = 0, 1
    while True:

        nx = x + dv[d][0]
        ny = y + dv[d][1]

        if nx < 0 or nx >= R or ny < 0 or ny >= C:
            d = (d + 3) % 4     # 반시계 방향으로 회전
            continue

        else:
            if board[nx][ny] == -1: # 이동한 곳이 공청기라면
                return
            
            tmp = board[nx][ny] # 현재 위치의 값 저장
            board[nx][ny] = v     # 이전 위치값 변경
            v = tmp             # 현재 위치에 원래 있던 값으로 업데이트
            x, y = nx, ny

v 는 바뀔 값, d는 방향이다.

down에서 바뀌는 부분은 진행하다 board의 끝에 다다랐을 때이다. 

up에서는 아래와 같이 반시계 방향으로 회전시키고,

        if nx < 0 or nx >= R or ny < 0 or ny >= C:
            d = (d + 3) % 4     # 반시계 방향으로 회전
            continue

 

down에서는 시계 방향으로 회전시킨다.

        if nx < 0 or nx >= R or ny < 0 or ny >= C:
            d = (d + 1) % 4     # 시계 방향으로 회전
            continue

 

전체 코드

# 3:37 ~ 5:08 (1'31")

from collections import deque

dv = [(-1, 0), (0,1), (1,0), (0,-1)]
R, C, T = map(int, input().split())

board = [[int(x) for x in input().split()] for _ in range(R)]


def getDustPos():
    dusts = []
    for i in range(R):
        for j in range(C):
            if board[i][j] > 0:
                dusts.append((i,j, board[i][j]))
    
    return dusts

def getCleanerPos():
    for i in range(R):
        if board[i][0] == -1:
            return i

def getAllDusts():
    total = 0
    for i in range(R):
        for j in range(C):
            if board[i][j] > 0:
                total += board[i][j]
    return total

def bfs(R, C):
    global board
    queue = deque(getDustPos())

    while queue:
        x, y, v = queue.popleft()

        cnt = 0
        tmp = v // 5
        for d in dv:
            nx = x + d[0]
            ny = y + d[1]

            if nx < 0 or nx >= R or ny < 0 or ny >= C or board[nx][ny] == -1:
                continue
            
            board[nx][ny] += tmp
            cnt += 1
        
        board[x][y] -= tmp * cnt

def up_cleaner(x, y):
    global board
    v, d = 0, 1
    while True:

        nx = x + dv[d][0]
        ny = y + dv[d][1]

        if nx < 0 or nx >= R or ny < 0 or ny >= C:
            d = (d + 3) % 4     # 반시계 방향으로 회전
            continue

        else:
            if board[nx][ny] == -1: # 이동한 곳이 공청기라면
                return
            
            tmp = board[nx][ny] # 현재 위치의 값 저장
            board[nx][ny] = v     # 이전 위치값 변경
            v = tmp             # 현재 위치에 원래 있던 값으로 업데이트
            x, y = nx, ny

def down_cleaner(x, y):
    global board
    v, d = 0, 1
    while True:

        nx = x + dv[d][0]
        ny = y + dv[d][1]

        if nx < 0 or nx >= R or ny < 0 or ny >= C:
            d = (d + 1) % 4     # 시계 방향으로 회전
            continue

        else:
            if board[nx][ny] == -1:
                return
            tmp = board[nx][ny] # 현재 위치의 값 저장
            board[nx][ny] = v     # 이전 위치값 변경
            v = tmp             # 현재 위치에 원래 있던 값으로 업데이트
            x, y = nx, ny


cleaner_x = getCleanerPos() # 위 공청기 x좌표, y좌표

for i in range(T):

    bfs(R, C) # 먼지 동시 확산

    up_cleaner(cleaner_x, 0)
    down_cleaner(cleaner_x + 1, 0)

print(getAllDusts())

 

오랜만에 풀이 코드가 깔끔해서 좋다 ㅎㅎㅎ!!